From 5941e962ecf9bdbac330f7ca731d9a3b54904a38 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Mon, 2 Nov 2020 21:21:18 +0000 Subject: [PATCH 01/82] wip --- .../themoviedb/themoviedb.component.html | 22 +++++++++++++++++-- .../themoviedb/themoviedb.component.ts | 9 ++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.html b/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.html index 9b325f299..85ffd4064 100644 --- a/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.html +++ b/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.html @@ -15,7 +15,7 @@ Excluded Keyword IDs for Movie Suggestions
- + + + + +
+ + + + + {{option.name}} + + + + - - - -
- - - - - {{option.name}} - - - - - - - - -
-
-
-
-
- - + \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.ts b/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.ts index 892ff6755..228d4fc42 100644 --- a/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.ts +++ b/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.ts @@ -1,15 +1,13 @@ import {COMMA, ENTER} from "@angular/cdk/keycodes"; import { Component, OnInit, ElementRef, ViewChild } from "@angular/core"; -import { MatChipInputEvent } from "@angular/material/chips"; -import { MatAutocompleteSelectedEvent, MatAutocomplete } from "@angular/material/autocomplete"; -import { empty, of, Observable } from "rxjs"; +import { MatAutocomplete } from "@angular/material/autocomplete"; import { ITheMovieDbSettings, IMovieDbKeyword } from "../../interfaces"; import { NotificationService } from "../../services"; import { SettingsService } from "../../services"; import { TheMovieDbService } from "../../services"; -import { FormControl, FormBuilder, FormGroup } from "@angular/forms"; -import { startWith, map, debounceTime, tap, switchMap, finalize } from "rxjs/operators"; +import { FormBuilder, FormGroup } from "@angular/forms"; +import { debounceTime, switchMap } from "rxjs/operators"; interface IKeywordTag { id: number; @@ -28,9 +26,6 @@ export class TheMovieDbComponent implements OnInit { public tagForm: FormGroup; public filteredTags: IMovieDbKeyword[]; @ViewChild('fruitInput') public fruitInput: ElementRef; - @ViewChild('auto') public matAutocomplete: MatAutocomplete; - - private readonly separatorKeysCodes: number[] = [ENTER, COMMA]; constructor(private settingsService: SettingsService, private notificationService: NotificationService, @@ -50,6 +45,13 @@ export class TheMovieDbComponent implements OnInit { initial: true, })) : []; + this.excludedKeywords.forEach(key => { + this.tmdbService.getKeyword(key.id).subscribe(keyResult => { + this.excludedKeywords.filter((val, idx) => { + val.name = keyResult.name; + }) + }); + }); }); this.tagForm @@ -64,61 +66,8 @@ export class TheMovieDbComponent implements OnInit { ) .subscribe((r) => (this.filteredTags = r)); - // this.tagForm.controls.input.valueChanges - // .pipe( - // debounceTime(500), - // switchMap(value => this.tmdbService.getKeywords(value)) - // ) - // .subscribe((data: IMovieDbKeyword[]) => { - // this.filteredTags = data; - // }); } - public async selected(event: MatAutocompleteSelectedEvent) { - const keywordId = await this.tmdbService.getKeyword(+event.option.value).toPromise(); - this.excludedKeywords.push({ id: keywordId.id, name: keywordId.name, initial: false}); - this.fruitInput.nativeElement.value = ''; - this.tagForm.controls.input.setValue(null); - } - - public autocompleteKeyword = (text: string) => this.tmdbService.getKeywords(text); - - public onAddingKeyword = (tag: string | IKeywordTag) => { - if (typeof tag === "string") { - const id = Number(tag); - return isNaN(id) ? empty() : this.tmdbService.getKeyword(id); - } else { - return of(tag); - } - } - - public onKeywordSelect = (keyword: IKeywordTag) => { - if (keyword.initial) { - this.tmdbService.getKeyword(keyword.id) - .subscribe(k => { - keyword.name = k.name; - keyword.initial = false; - }); - } - } - - public async add(event: MatChipInputEvent) { - const input = event.input; - const value = event.value; - - // Add our fruit - if ((value || '').trim()) { - const keyword = await this.tmdbService.getKeywords(value).toPromise(); - this.excludedKeywords.push({ id: keyword[0].id, name: keyword[0].name, initial: false }); - } - - // Reset the input value - if (input) { - input.value = ''; - } - this.tagForm.controls.input.setValue(null); - } - public remove(tag: IKeywordTag): void { const index = this.excludedKeywords.indexOf(tag); From 3aa28a58e5f69f8967db9c99517094b5067aa944 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 3 Nov 2020 08:20:26 +0000 Subject: [PATCH 03/82] Fixed unit tests --- .../Rule/Search/EmbyAvailabilityRuleTests.cs | 12 +++++++----- src/Ombi.Helpers/EmbyHelper.cs | 2 +- .../Context/Sqlite/SettingsSqliteContext.cs | 4 +++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs index becbeadc9..8e5c57d67 100644 --- a/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs +++ b/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs @@ -54,14 +54,15 @@ namespace Ombi.Core.Tests.Rule.Search { new EmbyServers { - ServerHostname = "http://test.com/" + ServerHostname = "http://test.com/", + ServerId = "8" } } }); ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny())).ReturnsAsync(new EmbyContent { ProviderId = "123", - EmbyId = 1.ToString() + EmbyId = 1.ToString(), }); var search = new SearchMovieViewModel() { @@ -70,7 +71,7 @@ namespace Ombi.Core.Tests.Rule.Search var result = await Rule.Execute(search); Assert.True(result.Success); - Assert.That(search.EmbyUrl, Is.EqualTo("http://test.com/#!/item?id=1")); + Assert.That(search.EmbyUrl, Is.EqualTo("http://test.com/web/index.html#!/item?id=1&serverId=8")); } [Test] @@ -83,7 +84,8 @@ namespace Ombi.Core.Tests.Rule.Search { new EmbyServers { - ServerHostname = string.Empty + ServerHostname = string.Empty, + ServerId = "8" } } }); @@ -99,7 +101,7 @@ namespace Ombi.Core.Tests.Rule.Search var result = await Rule.Execute(search); Assert.True(result.Success); - Assert.That(search.EmbyUrl, Is.EqualTo("https://app.emby.media/#!/item?id=1")); + Assert.That(search.EmbyUrl, Is.EqualTo("https://app.emby.media/web/index.html#!/item?id=1&serverId=8")); } [Test] diff --git a/src/Ombi.Helpers/EmbyHelper.cs b/src/Ombi.Helpers/EmbyHelper.cs index 4d2bcfc57..785ca47d4 100644 --- a/src/Ombi.Helpers/EmbyHelper.cs +++ b/src/Ombi.Helpers/EmbyHelper.cs @@ -1,6 +1,6 @@ namespace Ombi.Helpers { - public class EmbyHelper + public static class EmbyHelper { public static string GetEmbyMediaUrl(string mediaId, string serverId, string customerServerUrl = null, bool isJellyfin = false) { diff --git a/src/Ombi.Store/Context/Sqlite/SettingsSqliteContext.cs b/src/Ombi.Store/Context/Sqlite/SettingsSqliteContext.cs index 4245ecb1e..9be3d84fb 100644 --- a/src/Ombi.Store/Context/Sqlite/SettingsSqliteContext.cs +++ b/src/Ombi.Store/Context/Sqlite/SettingsSqliteContext.cs @@ -23,7 +23,9 @@ namespace Ombi.Store.Context.Sqlite Database.ExecuteSqlRaw(@"INSERT OR IGNORE INTO __EFMigrationsHistory (MigrationId,ProductVersion) VALUES('20191103205204_Inital', '2.2.6-servicing-10079'); "); } - catch (Exception) +#pragma warning disable RCS1075 // Avoid empty catch clause that catches System.Exception. + catch (Exception) +#pragma warning restore RCS1075 // Avoid empty catch clause that catches System.Exception. { // ignored } From 75d12679024ff133ac9f4a47a6a8bf0cf755ef85 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 3 Nov 2020 21:48:17 +0000 Subject: [PATCH 04/82] did some settings stuff! --- .../src/app/my-nav/nav-search.component.html | 2 +- .../src/app/my-nav/nav-search.component.ts | 1 + .../authentication.component.html | 6 +- .../customization.component.html | 12 ++-- .../app/settings/issues/issues.component.html | 12 ++-- .../landingpage/landingpage.component.html | 12 ++-- .../notifications/cloudmobile.component.html | 4 +- .../notifications/newsletter.component.html | 59 ++++++++++--------- .../src/app/settings/ombi/ombi.component.html | 29 ++++----- .../themoviedb/themoviedb.component.html | 2 +- .../usermanagement.component.html | 12 ++-- .../src/app/settings/vote/vote.component.html | 2 +- 12 files changed, 77 insertions(+), 76 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.html b/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.html index a83b7e6c8..ccf574725 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.html +++ b/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.html @@ -10,7 +10,7 @@ aria-label="Search" [ngbTypeahead]="searchModel" [resultFormatter]="formatter" [
- + diff --git a/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.ts b/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.ts index 10fed25a4..41ca0e926 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.ts +++ b/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.ts @@ -58,6 +58,7 @@ export class NavSearchComponent implements OnInit { } public selected(event: MatAutocompleteSelectedEvent) { + this.searchForm.controls.input.setValue(null); const val = event.option.value as IMultiSearchResult; if (val.mediaType == "movie") { this.router.navigate([`details/movie/${val.id}`]); diff --git a/src/Ombi/ClientApp/src/app/settings/authentication/authentication.component.html b/src/Ombi/ClientApp/src/app/settings/authentication/authentication.component.html index 7af303f97..1d6b6f5d3 100644 --- a/src/Ombi/ClientApp/src/app/settings/authentication/authentication.component.html +++ b/src/Ombi/ClientApp/src/app/settings/authentication/authentication.component.html @@ -7,14 +7,14 @@
- - Allow users to login without a password + + Allow users to login without a password
- Enable Plex OAuth + Enable Plex OAuth
diff --git a/src/Ombi/ClientApp/src/app/settings/customization/customization.component.html b/src/Ombi/ClientApp/src/app/settings/customization/customization.component.html index a096fd4ab..55f4a1b42 100644 --- a/src/Ombi/ClientApp/src/app/settings/customization/customization.component.html +++ b/src/Ombi/ClientApp/src/app/settings/customization/customization.component.html @@ -32,14 +32,14 @@
- + Hide Available Content On The Discover Page - +
- + Enable Custom Donation Link - +
@@ -54,9 +54,9 @@
- + Enable Custom Page - +
diff --git a/src/Ombi/ClientApp/src/app/settings/issues/issues.component.html b/src/Ombi/ClientApp/src/app/settings/issues/issues.component.html index d9ba9e342..702ca6915 100644 --- a/src/Ombi/ClientApp/src/app/settings/issues/issues.component.html +++ b/src/Ombi/ClientApp/src/app/settings/issues/issues.component.html @@ -9,21 +9,21 @@
- - Enable + + Enable
- - Enable In Progress State + + Enable In Progress State
- - Delete issues after they have been resolved + + Delete issues after they have been resolved
diff --git a/src/Ombi/ClientApp/src/app/settings/landingpage/landingpage.component.html b/src/Ombi/ClientApp/src/app/settings/landingpage/landingpage.component.html index a6c3f888d..91b923833 100644 --- a/src/Ombi/ClientApp/src/app/settings/landingpage/landingpage.component.html +++ b/src/Ombi/ClientApp/src/app/settings/landingpage/landingpage.component.html @@ -5,9 +5,9 @@ Landing Page Configuration
- + Enable - + -

Notice Message

- -
+ + Notice Message + + +

Notice Preview:

diff --git a/src/Ombi/ClientApp/src/app/settings/notifications/cloudmobile.component.html b/src/Ombi/ClientApp/src/app/settings/notifications/cloudmobile.component.html index c34f61687..a569a6ef4 100644 --- a/src/Ombi/ClientApp/src/app/settings/notifications/cloudmobile.component.html +++ b/src/Ombi/ClientApp/src/app/settings/notifications/cloudmobile.component.html @@ -15,8 +15,8 @@ - - + + diff --git a/src/Ombi/ClientApp/src/app/settings/notifications/newsletter.component.html b/src/Ombi/ClientApp/src/app/settings/notifications/newsletter.component.html index de484e726..80aeba34f 100644 --- a/src/Ombi/ClientApp/src/app/settings/notifications/newsletter.component.html +++ b/src/Ombi/ClientApp/src/app/settings/notifications/newsletter.component.html @@ -5,49 +5,52 @@
Newsletter
+
- + Enable +
+
+ +
+
+ Disable TV
- -
-
-
-
- + Disable Movies
- + Disable Music
- -
- -
+ + Subject + +
- -
- -
+ + Message + +
- - - + + - - +
@@ -62,15 +65,15 @@

-
- -
- + + Add External Email (For users that are not in Ombi) + +
- +
@@ -80,7 +83,7 @@ {{email}}
- +
diff --git a/src/Ombi/ClientApp/src/app/settings/ombi/ombi.component.html b/src/Ombi/ClientApp/src/app/settings/ombi/ombi.component.html index 9932e137e..63d9ce2a1 100644 --- a/src/Ombi/ClientApp/src/app/settings/ombi/ombi.component.html +++ b/src/Ombi/ClientApp/src/app/settings/ombi/ombi.component.html @@ -18,27 +18,22 @@ -
- - Do not send Notifications if a User has the Auto Approve permission + + Do not send Notifications if a User has the Auto Approve permission
- + Hide requests from other users - +
- + Auto Delete Available Requests - +
@@ -47,20 +42,20 @@
- + Ignore any certificate errors (Please restart after changing) - +
- + Allow us to collect anonymous analytical data e.g. browser used - +
diff --git a/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.html b/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.html index 1ad5d445a..57f25a1c4 100644 --- a/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.html +++ b/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.html @@ -1,6 +1,6 @@ 
-Upgraded
+
The Movie Database
diff --git a/src/Ombi/ClientApp/src/app/settings/usermanagement/usermanagement.component.html b/src/Ombi/ClientApp/src/app/settings/usermanagement/usermanagement.component.html index c8365eead..870bd45ae 100644 --- a/src/Ombi/ClientApp/src/app/settings/usermanagement/usermanagement.component.html +++ b/src/Ombi/ClientApp/src/app/settings/usermanagement/usermanagement.component.html @@ -9,13 +9,13 @@
- - Import Plex Users + + Import Plex Users
- Import Plex Admin + Import Plex Admin

Plex Users excluded from Import

@@ -25,7 +25,7 @@
- Import Emby Users + Import Emby Users
@@ -42,8 +42,8 @@
- - {{c.value | humanize}} + + {{c.value | humanize}}
diff --git a/src/Ombi/ClientApp/src/app/settings/vote/vote.component.html b/src/Ombi/ClientApp/src/app/settings/vote/vote.component.html index c8a0b5d25..a556fa048 100644 --- a/src/Ombi/ClientApp/src/app/settings/vote/vote.component.html +++ b/src/Ombi/ClientApp/src/app/settings/vote/vote.component.html @@ -8,7 +8,7 @@
- Enable + Enable
From 4e1f529afc98a6605baae891d217179d6a386f2b Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 3 Nov 2020 22:41:14 +0000 Subject: [PATCH 05/82] Added a little profile image, feedback would be good on this change, im not too sure --- src/Ombi/ClientApp/package.json | 1 + src/Ombi/ClientApp/src/app/app.component.html | 2 +- src/Ombi/ClientApp/src/app/auth/IUserLogin.ts | 1 + src/Ombi/ClientApp/src/app/auth/auth.service.ts | 3 ++- .../ClientApp/src/app/my-nav/my-nav.component.html | 3 +++ .../ClientApp/src/app/my-nav/my-nav.component.scss | 10 +++++++++- src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts | 7 +++++++ .../app/usermanagement/usermanagement.component.html | 2 +- src/Ombi/ClientApp/yarn.lock | 5 +++++ src/Ombi/Controllers/V1/TokenController.cs | 4 ++++ 10 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/Ombi/ClientApp/package.json b/src/Ombi/ClientApp/package.json index 0b3cc9e9b..b795819d9 100644 --- a/src/Ombi/ClientApp/package.json +++ b/src/Ombi/ClientApp/package.json @@ -55,6 +55,7 @@ "rxjs": "^6.5.2", "spinkit": "^1.2.5", "store": "^2.0.12", + "ts-md5": "^1.2.7", "tslib": "^1.10.0", "tslint-angular": "^1.1.2", "zone.js": "~0.10.2" diff --git a/src/Ombi/ClientApp/src/app/app.component.html b/src/Ombi/ClientApp/src/app/app.component.html index 09ec98ef9..5f074e17e 100644 --- a/src/Ombi/ClientApp/src/app/app.component.html +++ b/src/Ombi/ClientApp/src/app/app.component.html @@ -170,7 +170,7 @@
- + diff --git a/src/Ombi/ClientApp/src/app/auth/IUserLogin.ts b/src/Ombi/ClientApp/src/app/auth/IUserLogin.ts index 4a24798d4..e63ac3552 100644 --- a/src/Ombi/ClientApp/src/app/auth/IUserLogin.ts +++ b/src/Ombi/ClientApp/src/app/auth/IUserLogin.ts @@ -12,4 +12,5 @@ export interface ILocalUser { roles: string[]; name: string; username:string; + email: string; } diff --git a/src/Ombi/ClientApp/src/app/auth/auth.service.ts b/src/Ombi/ClientApp/src/app/auth/auth.service.ts index 73e4388ec..3eace43a4 100644 --- a/src/Ombi/ClientApp/src/app/auth/auth.service.ts +++ b/src/Ombi/ClientApp/src/app/auth/auth.service.ts @@ -52,8 +52,9 @@ export class AuthService extends ServiceHelpers { const json = this.jwtHelperService.decodeToken(token); const roles = json.role; const name = json.sub; + const email = json.Email; - const u = { name, roles: [] as string[] }; + const u = { name, roles: [] as string[], email }; if (roles instanceof Array) { u.roles = roles; } else { diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html index e95ceda1c..3ab703851 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html @@ -3,6 +3,9 @@ [attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'" [mode]="(isHandset$ | async) ? 'over' : 'side'" [opened]="!(isHandset$ | async)"> {{applicationName}} +
+ +
diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.scss b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.scss index ebc723472..8225d8742 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.scss +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.scss @@ -87,4 +87,12 @@ /*bottom-nav-link:hover{ background-color:rgb(226, 52, 36) !important; -}*/ \ No newline at end of file +}*/ + +.profile-img-container { + text-align: center;padding-bottom: 15px; +} +.profile-img { + width: 100px; + border-radius: 100%; +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts index 1e2c1988b..9560ec507 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts @@ -7,6 +7,7 @@ import { StorageService } from '../shared/storage/storage-service'; import { SettingsService } from '../services'; import { MatSlideToggleChange } from '@angular/material/slide-toggle'; import { SearchFilter } from './SearchFilter'; +import {Md5} from 'ts-md5/dist/md5'; export enum SearchFilterType { Movie = 1, @@ -31,6 +32,7 @@ export class MyNavComponent implements OnInit { @Input() public applicationName: string; @Input() public username: string; @Input() public isAdmin: string; + @Input() public email: string; @Output() public logoutClick = new EventEmitter(); @Output() public themeChange = new EventEmitter(); public theme: string; @@ -38,6 +40,7 @@ export class MyNavComponent implements OnInit { public navItems: INavBar[]; public searchFilter: SearchFilter; public SearchFilterType = SearchFilterType; + public emailHash: string|Int32Array; constructor(private breakpointObserver: BreakpointObserver, private settingsService: SettingsService, @@ -53,6 +56,10 @@ export class MyNavComponent implements OnInit { tvShows: true } + + const md5 = new Md5(); + this.emailHash = md5.appendStr(this.email).end(); + this.issuesEnabled = await this.settingsService.issueEnabled().toPromise(); const customizationSettings = await this.settingsService.getCustomization().toPromise(); console.log("issues enabled: " + this.issuesEnabled); diff --git a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html index 92a98cc76..40c9e9f6c 100644 --- a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html +++ b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html @@ -8,7 +8,7 @@
- +
diff --git a/src/Ombi/ClientApp/yarn.lock b/src/Ombi/ClientApp/yarn.lock index 68a8a794f..b80bf9ef1 100644 --- a/src/Ombi/ClientApp/yarn.lock +++ b/src/Ombi/ClientApp/yarn.lock @@ -8713,6 +8713,11 @@ trim-newlines@^1.0.0: dependencies: glob "^7.1.2" +ts-md5@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/ts-md5/-/ts-md5-1.2.7.tgz#b76471fc2fd38f0502441f6c3b9494ed04537401" + integrity sha512-emODogvKGWi1KO1l9c6YxLMBn6CEH3VrH5mVPIyOtxBG52BvV4jP3GWz6bOZCz61nLgBc3ffQYE4+EHfCD+V7w== + ts-node@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-5.0.1.tgz#78e5d1cb3f704de1b641e43b76be2d4094f06f81" diff --git a/src/Ombi/Controllers/V1/TokenController.cs b/src/Ombi/Controllers/V1/TokenController.cs index e133a939d..ad6e8ca23 100644 --- a/src/Ombi/Controllers/V1/TokenController.cs +++ b/src/Ombi/Controllers/V1/TokenController.cs @@ -135,6 +135,10 @@ namespace Ombi.Controllers.V1 new Claim("Id", user.Id) }; claims.AddRange(roles.Select(role => new Claim("role", role))); + if(user.Email.HasValue()) + { + claims.Add(new Claim("Email", user.Email)); + } var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(StartupSingleton.Instance.SecurityKey)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); From b43d7c4ba69ec4d0d125d5e87839d8dcc6f0f59d Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 4 Nov 2020 08:09:07 +0000 Subject: [PATCH 06/82] fixed the error --- src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts index 9560ec507..2f25df92c 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts @@ -57,8 +57,10 @@ export class MyNavComponent implements OnInit { } - const md5 = new Md5(); - this.emailHash = md5.appendStr(this.email).end(); + if(this.email) { + const md5 = new Md5(); + this.emailHash = md5.appendStr(this.email).end(); + } this.issuesEnabled = await this.settingsService.issueEnabled().toPromise(); const customizationSettings = await this.settingsService.getCustomization().toPromise(); From 2fe5297f342aa3a065d2f29aa668d7d70c8340f2 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 4 Nov 2020 09:00:24 +0000 Subject: [PATCH 07/82] Improved the "user area" --- src/Ombi/ClientApp/src/app/app.component.ts | 5 ++- .../src/app/my-nav/my-nav.component.html | 8 ++++- .../src/app/my-nav/my-nav.component.scss | 31 +++++++++++++++++-- .../src/app/my-nav/my-nav.component.ts | 28 ++++++++++++++--- src/Ombi/wwwroot/translations/en.json | 5 ++- 5 files changed, 64 insertions(+), 13 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/app.component.ts b/src/Ombi/ClientApp/src/app/app.component.ts index abf47b1bf..9b12be5ef 100644 --- a/src/Ombi/ClientApp/src/app/app.component.ts +++ b/src/Ombi/ClientApp/src/app/app.component.ts @@ -56,6 +56,7 @@ export class AppComponent implements OnInit { if (this.authService.loggedIn()) { this.identity.getUser().subscribe(u => { + this.username = u.userName; if (u.language) { this.translate.use(u.language); } @@ -106,9 +107,7 @@ export class AppComponent implements OnInit { this.currentUrl = event.url; if (event instanceof NavigationStart) { this.user = this.authService.claims(); - if (this.user && this.user.username) { - this.username = this.user.username; - } + this.isAdmin = this.authService.hasRole("admin"); this.showNav = this.authService.loggedIn(); diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html index 3ab703851..7fdcc62b0 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html @@ -4,7 +4,13 @@ [opened]="!(isHandset$ | async)"> {{applicationName}}
- +
+ +
+
+

{{username}}

+

{{welcomeText | translate}}

+
diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.scss b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.scss index 8225d8742..86dc17f8d 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.scss +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.scss @@ -90,9 +90,34 @@ }*/ .profile-img-container { - text-align: center;padding-bottom: 15px; + margin: 20px auto 10px; + overflow: hidden; + text-align: center; + width: 80%; } .profile-img { - width: 100px; - border-radius: 100%; + float: left; + height: 40px; + margin-right: 15px; +} +.profile-info { + overflow: hidden; + text-align: left; +} +.profile-info > h3 { + font-size: 15px; + text-transform: capitalize; + // color: #333; + margin-bottom: 2px; +} +.profile-info > p { + // color: #333; + font-size: 11px; + margin-top: 5px; +} + +.profile-img img { + border-radius: 6px; + box-shadow: 4px 3px 6px 0 rgba(0,0,0,.2); + width: 45px; } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts index 2f25df92c..5483929ae 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts @@ -7,7 +7,8 @@ import { StorageService } from '../shared/storage/storage-service'; import { SettingsService } from '../services'; import { MatSlideToggleChange } from '@angular/material/slide-toggle'; import { SearchFilter } from './SearchFilter'; -import {Md5} from 'ts-md5/dist/md5'; +import { Md5 } from 'ts-md5/dist/md5'; +import { TranslateService } from '@ngx-translate/core'; export enum SearchFilterType { Movie = 1, @@ -40,11 +41,13 @@ export class MyNavComponent implements OnInit { public navItems: INavBar[]; public searchFilter: SearchFilter; public SearchFilterType = SearchFilterType; - public emailHash: string|Int32Array; + public emailHash: string | Int32Array; + public welcomeText: string; constructor(private breakpointObserver: BreakpointObserver, private settingsService: SettingsService, - private store: StorageService) { + private store: StorageService, + private translate: TranslateService) { } public async ngOnInit() { @@ -56,8 +59,8 @@ export class MyNavComponent implements OnInit { tvShows: true } - - if(this.email) { + this.setWelcomeText(); + if (this.email) { const md5 = new Md5(); this.emailHash = md5.appendStr(this.email).end(); } @@ -122,4 +125,19 @@ export class MyNavComponent implements OnInit { } this.store.save("searchFilter", JSON.stringify(this.searchFilter)); } + + private setWelcomeText() { + var d = new Date(); + var hour = d.getHours(); + + if (hour >= 0 && hour < 12) { + this.welcomeText = 'NavigationBar.MorningWelcome'; + } + if (hour >= 12 && hour < 18) { + this.welcomeText = 'NavigationBar.AfternoonWelcome'; + } + if (hour >= 18 && hour < 23) { + this.welcomeText = 'NavigationBar.EveningWelcome'; + } + } } diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index 592aa34f3..3f6df284a 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -75,7 +75,10 @@ "TvShows":"TV Shows", "Music":"Music", "People":"People" - } + }, + "MorningWelcome":"Good morning!", + "AfternoonWelcome":"Good afternoon!", + "EveningWelcome":"Good evening!" }, "Search": { "Title": "Search", From f11af94770601252ad051bd845b071e5001e0964 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 4 Nov 2020 09:24:00 +0000 Subject: [PATCH 08/82] fixed TheTvDb incorrect link --- .../components/shared/social-icons/social-icons.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html index e867ec4d0..f5772842b 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html @@ -6,7 +6,7 @@ target="_blank"> - + From 9438bbf81da41dce1180a9eef2d6148ea1982c79 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Thu, 5 Nov 2020 20:26:48 +0000 Subject: [PATCH 09/82] Fixed the bug where you could not delete music requests --- src/Ombi/ClientApp/src/app/services/request.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/services/request.service.ts b/src/Ombi/ClientApp/src/app/services/request.service.ts index 8ac15e4f6..c9d37db89 100644 --- a/src/Ombi/ClientApp/src/app/services/request.service.ts +++ b/src/Ombi/ClientApp/src/app/services/request.service.ts @@ -189,8 +189,8 @@ export class RequestService extends ServiceHelpers { return this.http.get(`${this.url}music/search/${search}`, {headers: this.headers}); } - public removeAlbumRequest(request: IAlbumRequest): any { - return this.http.delete(`${this.url}music/${request.id}`, {headers: this.headers}); + public removeAlbumRequest(request: number): any { + return this.http.delete(`${this.url}music/${request}`, {headers: this.headers}); } } From a991b2fff2539100661d85f7ad5a0fb88be9345c Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Thu, 5 Nov 2020 20:56:26 +0000 Subject: [PATCH 10/82] More fixes around music --- .../options/request-options.component.html | 7 +++++-- .../options/request-options.component.ts | 15 +++++++++++++++ .../components/requests-list.component.ts | 6 ++++++ .../src/app/requests-list/models/UpdateType.ts | 5 +++-- src/Ombi/Controllers/V2/RequestsController.cs | 2 +- src/Ombi/wwwroot/translations/en.json | 7 ++++++- 6 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/options/request-options.component.html b/src/Ombi/ClientApp/src/app/requests-list/components/options/request-options.component.html index a22c5a2ef..be749cc28 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/options/request-options.component.html +++ b/src/Ombi/ClientApp/src/app/requests-list/components/options/request-options.component.html @@ -1,8 +1,11 @@ - Delete Request + {{'Requests.RequestPanel.Delete' | translate}} - Approve Request + {{'Requests.RequestPanel.Approve' | translate}} + + + {{'Requests.RequestPanel.ChangeAvailability' | translate}} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/options/request-options.component.ts b/src/Ombi/ClientApp/src/app/requests-list/components/options/request-options.component.ts index 5a4ef4430..625efe31c 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/options/request-options.component.ts +++ b/src/Ombi/ClientApp/src/app/requests-list/components/options/request-options.component.ts @@ -9,6 +9,9 @@ import { UpdateType } from '../../models/UpdateType'; templateUrl: './request-options.component.html', }) export class RequestOptionsComponent { + + public RequestType = RequestType; + constructor(@Inject(MAT_BOTTOM_SHEET_DATA) public data: any, private requestService: RequestService, private bottomSheetRef: MatBottomSheetRef) { } @@ -41,4 +44,16 @@ export class RequestOptionsComponent { this.bottomSheetRef.dismiss({type: UpdateType.Approve}); return; } + + public async changeAvailability() { + if (this.data.type === RequestType.movie) { + await this.requestService.markMovieAvailable({id: this.data.id}).toPromise(); + } + if (this.data.type === RequestType.album) { + await this.requestService.markAlbumAvailable({id: this.data.id}).toPromise(); + } + + this.bottomSheetRef.dismiss({type: UpdateType.Availability}); + return; + } } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.ts b/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.ts index fc00c39d3..6cdd84af3 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.ts +++ b/src/Ombi/ClientApp/src/app/requests-list/components/requests-list.component.ts @@ -29,6 +29,12 @@ export class RequestsListComponent { event.onChange(); return; } + if (result.type == UpdateType.Availability) { + // Need to do this here, as the status is calculated on the server + event.request.requestStatus = 'Common.Available'; + event.onChange(); + return; + } }); } } diff --git a/src/Ombi/ClientApp/src/app/requests-list/models/UpdateType.ts b/src/Ombi/ClientApp/src/app/requests-list/models/UpdateType.ts index 647c4601d..3a0f690db 100644 --- a/src/Ombi/ClientApp/src/app/requests-list/models/UpdateType.ts +++ b/src/Ombi/ClientApp/src/app/requests-list/models/UpdateType.ts @@ -1,4 +1,5 @@ export enum UpdateType { - Delete, - Approve + Delete, + Approve, + Availability } \ No newline at end of file diff --git a/src/Ombi/Controllers/V2/RequestsController.cs b/src/Ombi/Controllers/V2/RequestsController.cs index 496e17cee..95ab96367 100644 --- a/src/Ombi/Controllers/V2/RequestsController.cs +++ b/src/Ombi/Controllers/V2/RequestsController.cs @@ -139,7 +139,7 @@ namespace Ombi.Controllers.V2 return await _tvRequestEngine.UpdateAdvancedOptions(options); } - [HttpGet("albums/available/{count:int}/{position:int}/{sort}/{sortOrder}")] + [HttpGet("album/available/{count:int}/{position:int}/{sort}/{sortOrder}")] public async Task> GetAvailableAlbumRequests(int count, int position, string sort, string sortOrder) { return await _musicRequestEngine.GetRequestsByStatus(count, position, sort, sortOrder, RequestStatus.Available); diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index 3f6df284a..665811489 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -175,7 +175,12 @@ "RequestsToDisplay": "Requests to display", "RequestsTitle": "Title", "Details": "Details", - "Options": "Options" + "Options": "Options", + "RequestPanel": { + "Delete":"Delete Request", + "Approve":"Approve Request", + "ChangeAvailability":"Mark Available" + } }, "Issues": { "Title": "Issues", From 86220bb2371abc8be434ee0e24f4d6306b0b1cf9 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Thu, 5 Nov 2020 21:14:53 +0000 Subject: [PATCH 11/82] Improved the user profile section --- .../src/app/my-nav/my-nav.component.html | 43 ++++++++++++------- .../src/app/my-nav/my-nav.component.scss | 6 +++ 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html index 7fdcc62b0..a7ac5b737 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html @@ -3,14 +3,17 @@ [attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'" [mode]="(isHandset$ | async) ? 'over' : 'side'" [opened]="!(isHandset$ | async)"> {{applicationName}} -
-
- -
-
-

{{username}}

-

{{welcomeText | translate}}

-
+
+
+
+ +
+
+

{{username}}

+

{{welcomeText | translate}}

+
+
@@ -67,13 +70,23 @@
- - - {{ 'NavigationBar.Filter.Movies' | translate}} - {{ 'NavigationBar.Filter.TvShows' | translate}} - {{ 'NavigationBar.Filter.Music' | translate}} - {{ 'NavigationBar.Filter.People' | translate}} - + + + + {{ 'NavigationBar.Filter.Movies' | translate}} + + {{ 'NavigationBar.Filter.TvShows' | translate}} + + {{ 'NavigationBar.Filter.Music' | translate}} + + {{ 'NavigationBar.Filter.People' | translate}} +
diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.scss b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.scss index 86dc17f8d..761b96620 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.scss +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.scss @@ -120,4 +120,10 @@ border-radius: 6px; box-shadow: 4px 3px 6px 0 rgba(0,0,0,.2); width: 45px; +} + +.outer-profile { + background-color: #303030; + box-shadow: inset 0 0 3px #000000; + padding: 1px; } \ No newline at end of file From 58704f00195a5ebc32c1cdf4f069033e015b5f35 Mon Sep 17 00:00:00 2001 From: Micky <60691199+AliMickey@users.noreply.github.com> Date: Fri, 6 Nov 2020 14:46:05 +1100 Subject: [PATCH 12/82] Update sonarr.component.html Fix unneeded div block and move v3 below enable. --- .../src/app/settings/sonarr/sonarr.component.html | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/settings/sonarr/sonarr.component.html b/src/Ombi/ClientApp/src/app/settings/sonarr/sonarr.component.html index 8d2339189..2e2e13170 100644 --- a/src/Ombi/ClientApp/src/app/settings/sonarr/sonarr.component.html +++ b/src/Ombi/ClientApp/src/app/settings/sonarr/sonarr.component.html @@ -6,15 +6,13 @@
-
-
- V3 -
-
Enable
+
+ V3 +
Advanced
@@ -175,4 +173,4 @@
-
\ No newline at end of file +
From 19933f81865ed8df4171cf45133b065cd049ec3d Mon Sep 17 00:00:00 2001 From: tidusjar Date: Fri, 6 Nov 2020 22:18:18 +0000 Subject: [PATCH 13/82] Sorted out the user management bulk edit --- src/Ombi/ClientApp/angular.json | 1 + src/Ombi/ClientApp/package.json | 4 +- src/Ombi/ClientApp/src/app/app.component.html | 2 +- src/Ombi/ClientApp/src/app/app.component.ts | 30 +++++++------- .../usermanagement.component.html | 40 ++++++++++--------- .../usermanagement.component.scss | 7 +++- .../usermanagement.component.ts | 5 +++ src/Ombi/ClientApp/src/tsconfig.app.json | 2 +- src/Ombi/ClientApp/yarn.lock | 23 +++++++---- 9 files changed, 66 insertions(+), 48 deletions(-) diff --git a/src/Ombi/ClientApp/angular.json b/src/Ombi/ClientApp/angular.json index 0d3e21b27..4038c9a56 100644 --- a/src/Ombi/ClientApp/angular.json +++ b/src/Ombi/ClientApp/angular.json @@ -28,6 +28,7 @@ "./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css", "src/styles/_imports.scss", "node_modules/bootstrap/scss/bootstrap.scss", + "node_modules/primeng/resources/themes/md-dark-deeppurple/theme.css", "node_modules/font-awesome/scss/font-awesome.scss", "node_modules/primeng/resources/primeng.min.css", "node_modules/primeicons/primeicons.css", diff --git a/src/Ombi/ClientApp/package.json b/src/Ombi/ClientApp/package.json index b795819d9..388815c78 100644 --- a/src/Ombi/ClientApp/package.json +++ b/src/Ombi/ClientApp/package.json @@ -50,8 +50,8 @@ "ngx-order-pipe": "^2.0.1", "please-wait": "^0.0.5", "popper.js": "^1.14.3", - "primeicons": "^1.0.0", - "primeng": "^9.0.6", + "primeicons": "^4.0.0", + "primeng": "^10.0.3", "rxjs": "^6.5.2", "spinkit": "^1.2.5", "store": "^2.0.12", diff --git a/src/Ombi/ClientApp/src/app/app.component.html b/src/Ombi/ClientApp/src/app/app.component.html index 5f074e17e..c9f18a210 100644 --- a/src/Ombi/ClientApp/src/app/app.component.html +++ b/src/Ombi/ClientApp/src/app/app.component.html @@ -170,7 +170,7 @@
- + diff --git a/src/Ombi/ClientApp/src/app/app.component.ts b/src/Ombi/ClientApp/src/app/app.component.ts index 9b12be5ef..6324f60d7 100644 --- a/src/Ombi/ClientApp/src/app/app.component.ts +++ b/src/Ombi/ClientApp/src/app/app.component.ts @@ -56,7 +56,6 @@ export class AppComponent implements OnInit { if (this.authService.loggedIn()) { this.identity.getUser().subscribe(u => { - this.username = u.userName; if (u.language) { this.translate.use(u.language); } @@ -107,28 +106,27 @@ export class AppComponent implements OnInit { this.currentUrl = event.url; if (event instanceof NavigationStart) { this.user = this.authService.claims(); + this.username = this.user.name; this.isAdmin = this.authService.hasRole("admin"); this.showNav = this.authService.loggedIn(); - // tslint:disable-next-line:no-string-literal - // if (this.user !== null && this.user.name && !this.checkedForUpdate && this.isAdmin) { - // this.checkedForUpdate = true; - // this.jobService.getCachedUpdate().subscribe(x => { - // this.updateAvailable = x; - // }, - // err => this.checkedForUpdate = true); - // } + if (this.authService.loggedIn()) { - if (this.authService.loggedIn() && !this.hubConnected) { - this.signalrNotification.initialize(); - this.hubConnected = true; + if (!this.isAdmin) { + // let's get the remaining requests etc + } - this.signalrNotification.Notification.subscribe(data => { - this.snackBar.open(data, "OK", { - duration: 3000 + if (!this.hubConnected) { + this.signalrNotification.initialize(); + this.hubConnected = true; + + this.signalrNotification.Notification.subscribe(data => { + this.snackBar.open(data, "OK", { + duration: 3000 + }); }); - }); + } } } }); diff --git a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html index 40c9e9f6c..4b64e2f45 100644 --- a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html +++ b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html @@ -121,33 +121,35 @@ - +

Bulk Edit


+
-
- - +
+ + {{c.value | humanize}} +
-
+
-
- -
- -
-
+ + Movie Request Limit + + + + Episode Request Limit + + + + Music Request Limit + + -
- -
- -
-
- - + +
diff --git a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.scss b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.scss index fc18d7781..19dab627d 100644 --- a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.scss +++ b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.scss @@ -35,6 +35,10 @@ opacity: 1; } +.full { + width: 100%; +} + .content { margin-top: 2em; } @@ -52,7 +56,8 @@ ::ng-deep .dark & button { background: $accent-dark !important; color: #303030 !important; - }/* + } + /* &:hover{ box-shadow: 0 1em 1em -0.8em #fff; transform: translateY(-0.50em); diff --git a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.ts b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.ts index 8ab01f665..4071c6b8f 100644 --- a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.ts +++ b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.ts @@ -26,6 +26,7 @@ export class UserManagementComponent implements OnInit, AfterViewInit { public availableClaims: ICheckbox[]; public bulkMovieLimit?: number; public bulkEpisodeLimit?: number; + public bulkMusicLimit?: number; public plexEnabled: boolean; constructor(private identityService: IdentityService, @@ -84,6 +85,9 @@ export class UserManagementComponent implements OnInit, AfterViewInit { if (this.bulkMovieLimit) { x.movieRequestLimit = this.bulkMovieLimit; } + if (this.bulkMusicLimit) { + x.musicRequestLimit = this.bulkMusicLimit; + } this.identityService.updateUser(x).subscribe(y => { if (!y.successful) { this.notificationService.error(`Could not update user ${x.userName}. Reason ${y.errors[0]}`); @@ -95,6 +99,7 @@ export class UserManagementComponent implements OnInit, AfterViewInit { this.showBulkEdit = false; this.bulkMovieLimit = undefined; this.bulkEpisodeLimit = undefined; + this.bulkMusicLimit = undefined; } public isAllSelected() { diff --git a/src/Ombi/ClientApp/src/tsconfig.app.json b/src/Ombi/ClientApp/src/tsconfig.app.json index 1a0bba757..aef5c882b 100644 --- a/src/Ombi/ClientApp/src/tsconfig.app.json +++ b/src/Ombi/ClientApp/src/tsconfig.app.json @@ -9,7 +9,7 @@ "moduleResolution": "node", "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "es5", + "target": "es2015", "types": ["node"], "resolveJsonModule":true, "allowSyntheticDefaultImports":true, diff --git a/src/Ombi/ClientApp/yarn.lock b/src/Ombi/ClientApp/yarn.lock index b80bf9ef1..113ad95c3 100644 --- a/src/Ombi/ClientApp/yarn.lock +++ b/src/Ombi/ClientApp/yarn.lock @@ -6981,15 +6981,17 @@ prepend-http@^1.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= -primeicons@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/primeicons/-/primeicons-1.0.0.tgz#90061f168ef6227f21f0a7db8204ffa85cd27aec" - integrity sha512-p/hzIjUVccW4eJPhuORHI3AUkDpqfvCQVrjxbFEejnTEdWY4C8fomVfjiaA9jCu83fSQnBHuRIGB96iAR8R6uA== +primeicons@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/primeicons/-/primeicons-4.0.0.tgz#a3594b3af213dcf8c4c3d6fc99eea05b7c92f57c" + integrity sha512-JQBIswGSItn8I0Pq21RchENpKJxSi1MjfBDfggMQpXtoKNKblJoHmol/7tCV3CAV2Dlb94ht8TD8qdIAW01pGg== -primeng@^9.0.6: - version "9.0.6" - resolved "https://registry.yarnpkg.com/primeng/-/primeng-9.0.6.tgz#b0d8350428722aaab2f5d4a65d3d06b5d4f673e0" - integrity sha512-RGVTkXwHTb7609zTU/uJFYdbJV124kOwrMiwnHjFn6UkwmS3yTX0Kkr21v8KOTJqsKLq3OWVYhkOwvUklipAAg== +primeng@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/primeng/-/primeng-10.0.3.tgz#e9d0ea425b9c5023bc9eef2cb014941939d0c35e" + integrity sha512-Nsiwpmy3RlFPBlxabdzeAYxFn4fXEyZjj7iAi1X5J4RRGD7NoB67+NbnOInE1rXTnNVHYxCca91OvaNiHSWWrg== + dependencies: + tslib "^2.0.0" private@^0.1.8: version "0.1.8" @@ -8745,6 +8747,11 @@ tslib@^1.8.0, tslib@^1.8.1: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" +tslib@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" + integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== + tslint-angular@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/tslint-angular/-/tslint-angular-1.1.2.tgz#5ce7020968e3b9dc7a40b6d15dadd6da34787309" From 8d05dfc4c262c6b59797fb5b7ef6c2e76436ec32 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Fri, 6 Nov 2020 23:16:14 +0000 Subject: [PATCH 14/82] Added the remaining requests into the navbar, just need to dynamically update it now --- src/Ombi/ClientApp/src/app/app.module.ts | 7 +- .../src/app/my-nav/my-nav.component.html | 13 ++- .../src/app/my-nav/my-nav.component.scss | 27 +++--- .../src/app/my-nav/my-nav.component.ts | 6 +- .../src/app/my-nav/nav-search.component.ts | 4 +- .../remaining-requests.component.html | 7 ++ .../remaining-requests.component.ts | 89 +++++++++++++++++++ 7 files changed, 128 insertions(+), 25 deletions(-) create mode 100644 src/Ombi/ClientApp/src/app/shared/remaining-requests/remaining-requests.component.html create mode 100644 src/Ombi/ClientApp/src/app/shared/remaining-requests/remaining-requests.component.ts diff --git a/src/Ombi/ClientApp/src/app/app.module.ts b/src/Ombi/ClientApp/src/app/app.module.ts index 2a9b6d802..09e5f2955 100644 --- a/src/Ombi/ClientApp/src/app/app.module.ts +++ b/src/Ombi/ClientApp/src/app/app.module.ts @@ -53,7 +53,7 @@ import { TokenResetPasswordComponent } from "./login/tokenresetpassword.componen // Services import { AuthGuard } from "./auth/auth.guard"; import { AuthService } from "./auth/auth.service"; -import { ImageService, SettingsService, CustomPageService } from "./services"; +import { ImageService, SettingsService, CustomPageService, RequestService } from "./services"; import { LandingPageService } from "./services"; import { NotificationService } from "./services"; import { IssuesService, JobService, PlexTvService, StatusService, SearchService, IdentityService, MessageService } from "./services"; @@ -65,6 +65,8 @@ import { OverlayModule } from "@angular/cdk/overlay"; import { StorageService } from "./shared/storage/storage-service"; import { SignalRNotificationService } from "./services/signlarnotification.service"; import { MatMenuModule } from "@angular/material/menu"; +import { RemainingRequestsComponent } from "./shared/remaining-requests/remaining-requests.component"; + const routes: Routes = [ { path: "*", component: PageNotFoundComponent }, { path: "", redirectTo: "/discover", pathMatch: "full" }, @@ -168,7 +170,9 @@ export function JwtTokenGetter() { LoginOAuthComponent, MyNavComponent, NavSearchComponent, + RemainingRequestsComponent, ], + providers: [ NotificationService, AuthService, @@ -187,6 +191,7 @@ export function JwtTokenGetter() { SearchV2Service, MessageService, StorageService, + RequestService, SignalRNotificationService, { provide: APP_BASE_HREF, diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html index a7ac5b737..1e44c367b 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html @@ -2,7 +2,9 @@ + {{applicationName}} +
+ + diff --git a/src/Ombi/ClientApp/src/app/shared/remaining-requests/remaining-requests.component.ts b/src/Ombi/ClientApp/src/app/shared/remaining-requests/remaining-requests.component.ts new file mode 100644 index 000000000..222389e5e --- /dev/null +++ b/src/Ombi/ClientApp/src/app/shared/remaining-requests/remaining-requests.component.ts @@ -0,0 +1,89 @@ +import { Component, Input, OnInit } from "@angular/core"; +import { TranslateService } from "@ngx-translate/core"; +import { RequestType } from "../../interfaces"; +import { IRemainingRequests } from "../../interfaces/IRemainingRequests"; +import { RequestService } from "../../services"; +@Component({ + selector: "app-remaining-requests", + templateUrl: "remaining-requests.component.html", + styles: [`.mat-icon { + vertical-align: middle; + }`], +}) +export class RemainingRequestsComponent implements OnInit { + + @Input() type: RequestType; + public RequestType = RequestType; + public remaining: IRemainingRequests; + public daysUntil: number; + public hoursUntil: number; + public minutesUntil: number; + public matIcon: string; + + constructor(private requestService: RequestService, + private translate: TranslateService) { } + + public ngOnInit(): void { + this.start(); + } + + public getTooltipContent() : string { + if (this.daysUntil > 1) { + return this.translate.instant('Requests.Remaining.NextDays', { time: this.daysUntil}); + } + if (this.hoursUntil > 1 && this.daysUntil <= 1) { + return this.translate.instant('Requests.Remaining.NextHours', { time: this.hoursUntil}); + } + if (this.minutesUntil >= 1 && this.hoursUntil <= 1 && this.daysUntil <= 1) { + return this.minutesUntil == 1 + ? this.translate.instant('Requests.Remaining.NextMinute', { time: this.minutesUntil}) + : this.translate.instant('Requests.Remaining.NextMinutes', { time: this.minutesUntil}); + } + } + + private start() { + + const callback = (remaining => { + this.remaining = remaining; + if (this.remaining && this.remaining.hasLimit) { + this.calculateTime(); + } + }); + + switch (this.type) { + case RequestType.movie: + this.requestService.getRemainingMovieRequests().subscribe(callback); + this.matIcon = "movie"; + + break; + case RequestType.tvShow: + this.requestService.getRemainingTvRequests().subscribe(callback); + this.matIcon = "tv"; + + break; + case RequestType.album: + this.requestService.getRemainingMusicRequests().subscribe(callback); + this.matIcon = "library_music"; + + break; + } + } + + private calculateTime(): void { + this.daysUntil = Math.ceil(this.daysUntilNextRequest()); + this.hoursUntil = Math.ceil(this.hoursUntilNextRequest()); + this.minutesUntil = Math.ceil(this.minutesUntilNextRequest()); + } + + private daysUntilNextRequest(): number { + return (new Date(this.remaining.nextRequest).getTime() - new Date().getTime()) / 1000 / 60 / 60 / 24; + } + + private hoursUntilNextRequest(): number { + return (new Date(this.remaining.nextRequest).getTime() - new Date().getTime()) / 1000 / 60 / 60; + } + + private minutesUntilNextRequest(): number { + return (new Date(this.remaining.nextRequest).getTime() - new Date().getTime()) / 1000 / 60; + } +} From 0d55eec72ec37a6ebf6f31357adf44beea69fbb1 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 10 Nov 2020 19:22:34 +0000 Subject: [PATCH 15/82] Update to net5.0 --- .../Ombi.Api.CloudService.csproj | 4 ++-- .../Ombi.Api.CouchPotato.csproj | 4 ++-- src/Ombi.Api.Discord/Ombi.Api.Discord.csproj | 2 +- src/Ombi.Api.DogNzb/Ombi.Api.DogNzb.csproj | 4 ++-- src/Ombi.Api.Emby/Ombi.Api.Emby.csproj | 2 +- src/Ombi.Api.FanartTv/Ombi.Api.FanartTv.csproj | 2 +- src/Ombi.Api.Github/Ombi.Api.Github.csproj | 4 ++-- src/Ombi.Api.Gotify/Ombi.Api.Gotify.csproj | 4 ++-- src/Ombi.Api.GroupMe/Ombi.Api.GroupMe.csproj | 4 ++-- src/Ombi.Api.Lidarr/Ombi.Api.Lidarr.csproj | 4 ++-- .../Ombi.Api.Mattermost.csproj | 6 +++--- .../Ombi.Api.MusicBrainz.csproj | 2 +- .../Ombi.Api.Notifications.csproj | 4 ++-- src/Ombi.Api.Plex/Ombi.Api.Plex.csproj | 2 +- .../Ombi.Api.Pushbullet.csproj | 2 +- src/Ombi.Api.Pushover/Ombi.Api.Pushover.csproj | 4 ++-- src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj | 4 ++-- src/Ombi.Api.Service/Ombi.Api.Service.csproj | 4 ++-- src/Ombi.Api.SickRage/Ombi.Api.SickRage.csproj | 4 ++-- src/Ombi.Api.Slack/Ombi.Api.Slack.csproj | 2 +- src/Ombi.Api.Sonarr/Ombi.Api.Sonarr.csproj | 2 +- src/Ombi.Api.Telegram/Ombi.Api.Telegram.csproj | 4 ++-- src/Ombi.Api.Trakt/Ombi.Api.Trakt.csproj | 2 +- src/Ombi.Api.TvMaze/Ombi.Api.TvMaze.csproj | 2 +- src/Ombi.Api.Twilio/Ombi.Api.Twilio.csproj | 4 ++-- src/Ombi.Api.Webhook/Ombi.Api.Webhook.csproj | 2 +- src/Ombi.Api/Ombi.Api.csproj | 6 +++--- src/Ombi.Core.Tests/Ombi.Core.Tests.csproj | 4 ++-- src/Ombi.Core/Ombi.Core.csproj | 8 ++++---- .../Ombi.DependencyInjection.csproj | 6 +++--- src/Ombi.HealthChecks/Ombi.HealthChecks.csproj | 2 +- .../Ombi.Helpers.Tests.csproj | 4 ++-- src/Ombi.Helpers/Ombi.Helpers.csproj | 10 +++++----- src/Ombi.Hubs/Ombi.Hubs.csproj | 2 +- src/Ombi.Mapping/Ombi.Mapping.csproj | 2 +- .../Ombi.Notifications.Templates.csproj | 2 +- .../Ombi.Notifications.Tests.csproj | 4 ++-- .../Ombi.Notifications.csproj | 2 +- .../Ombi.Schedule.Tests.csproj | 4 ++-- .../Jobs/Couchpotato/CouchPotatoSync.cs | 2 +- .../Jobs/Lidarr/LidarrAlbumSync.cs | 3 ++- .../Jobs/Lidarr/LidarrArtistSync.cs | 4 ++-- .../Jobs/SickRage/SickRageSync.cs | 4 ++-- src/Ombi.Schedule/Ombi.Schedule.csproj | 2 +- .../Ombi.Settings.Tests.csproj | 4 ++-- src/Ombi.Settings/Ombi.Settings.csproj | 6 +++--- src/Ombi.Store/Ombi.Store.csproj | 12 ++++++------ src/Ombi.Test.Common/Ombi.Test.Common.csproj | 2 +- src/Ombi.Tests/Ombi.Tests.csproj | 6 +++--- .../Ombi.Api.TheMovieDb.csproj | 2 +- src/Ombi.Updater/Ombi.Updater.csproj | 18 +++++++++--------- .../src/app/my-nav/my-nav.component.ts | 2 +- src/Ombi/Ombi.csproj | 15 +++++++-------- src/Ombi/databasea.json | 14 ++++++++++++++ 54 files changed, 125 insertions(+), 111 deletions(-) create mode 100644 src/Ombi/databasea.json diff --git a/src/Ombi.Api.CloudService/Ombi.Api.CloudService.csproj b/src/Ombi.Api.CloudService/Ombi.Api.CloudService.csproj index 3c31ecb84..d92fd7c6d 100644 --- a/src/Ombi.Api.CloudService/Ombi.Api.CloudService.csproj +++ b/src/Ombi.Api.CloudService/Ombi.Api.CloudService.csproj @@ -1,7 +1,7 @@ - + - netstandard2.1 + net5.0 diff --git a/src/Ombi.Api.CouchPotato/Ombi.Api.CouchPotato.csproj b/src/Ombi.Api.CouchPotato/Ombi.Api.CouchPotato.csproj index 809404bd4..bdd16edf8 100644 --- a/src/Ombi.Api.CouchPotato/Ombi.Api.CouchPotato.csproj +++ b/src/Ombi.Api.CouchPotato/Ombi.Api.CouchPotato.csproj @@ -1,7 +1,7 @@ - + - netstandard2.1 + net5.0 8.0 diff --git a/src/Ombi.Api.Discord/Ombi.Api.Discord.csproj b/src/Ombi.Api.Discord/Ombi.Api.Discord.csproj index 5c67fe577..f76114f83 100644 --- a/src/Ombi.Api.Discord/Ombi.Api.Discord.csproj +++ b/src/Ombi.Api.Discord/Ombi.Api.Discord.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.DogNzb/Ombi.Api.DogNzb.csproj b/src/Ombi.Api.DogNzb/Ombi.Api.DogNzb.csproj index 809404bd4..bdd16edf8 100644 --- a/src/Ombi.Api.DogNzb/Ombi.Api.DogNzb.csproj +++ b/src/Ombi.Api.DogNzb/Ombi.Api.DogNzb.csproj @@ -1,7 +1,7 @@ - + - netstandard2.1 + net5.0 8.0 diff --git a/src/Ombi.Api.Emby/Ombi.Api.Emby.csproj b/src/Ombi.Api.Emby/Ombi.Api.Emby.csproj index 2bb0c6352..5457b0290 100644 --- a/src/Ombi.Api.Emby/Ombi.Api.Emby.csproj +++ b/src/Ombi.Api.Emby/Ombi.Api.Emby.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.FanartTv/Ombi.Api.FanartTv.csproj b/src/Ombi.Api.FanartTv/Ombi.Api.FanartTv.csproj index 5c67fe577..f76114f83 100644 --- a/src/Ombi.Api.FanartTv/Ombi.Api.FanartTv.csproj +++ b/src/Ombi.Api.FanartTv/Ombi.Api.FanartTv.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.Github/Ombi.Api.Github.csproj b/src/Ombi.Api.Github/Ombi.Api.Github.csproj index 809404bd4..bdd16edf8 100644 --- a/src/Ombi.Api.Github/Ombi.Api.Github.csproj +++ b/src/Ombi.Api.Github/Ombi.Api.Github.csproj @@ -1,7 +1,7 @@ - + - netstandard2.1 + net5.0 8.0 diff --git a/src/Ombi.Api.Gotify/Ombi.Api.Gotify.csproj b/src/Ombi.Api.Gotify/Ombi.Api.Gotify.csproj index 79414ab00..5f1d62c66 100644 --- a/src/Ombi.Api.Gotify/Ombi.Api.Gotify.csproj +++ b/src/Ombi.Api.Gotify/Ombi.Api.Gotify.csproj @@ -1,7 +1,7 @@ - + - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.GroupMe/Ombi.Api.GroupMe.csproj b/src/Ombi.Api.GroupMe/Ombi.Api.GroupMe.csproj index 809404bd4..bdd16edf8 100644 --- a/src/Ombi.Api.GroupMe/Ombi.Api.GroupMe.csproj +++ b/src/Ombi.Api.GroupMe/Ombi.Api.GroupMe.csproj @@ -1,7 +1,7 @@ - + - netstandard2.1 + net5.0 8.0 diff --git a/src/Ombi.Api.Lidarr/Ombi.Api.Lidarr.csproj b/src/Ombi.Api.Lidarr/Ombi.Api.Lidarr.csproj index 809404bd4..bdd16edf8 100644 --- a/src/Ombi.Api.Lidarr/Ombi.Api.Lidarr.csproj +++ b/src/Ombi.Api.Lidarr/Ombi.Api.Lidarr.csproj @@ -1,7 +1,7 @@ - + - netstandard2.1 + net5.0 8.0 diff --git a/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj b/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj index 2192220e6..344b2e504 100644 --- a/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj +++ b/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj @@ -1,7 +1,7 @@ - + - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 @@ -10,7 +10,7 @@ - + diff --git a/src/Ombi.Api.MusicBrainz/Ombi.Api.MusicBrainz.csproj b/src/Ombi.Api.MusicBrainz/Ombi.Api.MusicBrainz.csproj index 8b408314a..c703166d5 100644 --- a/src/Ombi.Api.MusicBrainz/Ombi.Api.MusicBrainz.csproj +++ b/src/Ombi.Api.MusicBrainz/Ombi.Api.MusicBrainz.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 8.0 diff --git a/src/Ombi.Api.Notifications/Ombi.Api.Notifications.csproj b/src/Ombi.Api.Notifications/Ombi.Api.Notifications.csproj index 002e44260..349da09d9 100644 --- a/src/Ombi.Api.Notifications/Ombi.Api.Notifications.csproj +++ b/src/Ombi.Api.Notifications/Ombi.Api.Notifications.csproj @@ -1,7 +1,7 @@ - + - netstandard2.1 + net5.0 8.0 diff --git a/src/Ombi.Api.Plex/Ombi.Api.Plex.csproj b/src/Ombi.Api.Plex/Ombi.Api.Plex.csproj index 5c67fe577..f76114f83 100644 --- a/src/Ombi.Api.Plex/Ombi.Api.Plex.csproj +++ b/src/Ombi.Api.Plex/Ombi.Api.Plex.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.Pushbullet/Ombi.Api.Pushbullet.csproj b/src/Ombi.Api.Pushbullet/Ombi.Api.Pushbullet.csproj index 5c67fe577..f76114f83 100644 --- a/src/Ombi.Api.Pushbullet/Ombi.Api.Pushbullet.csproj +++ b/src/Ombi.Api.Pushbullet/Ombi.Api.Pushbullet.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.Pushover/Ombi.Api.Pushover.csproj b/src/Ombi.Api.Pushover/Ombi.Api.Pushover.csproj index 79414ab00..5f1d62c66 100644 --- a/src/Ombi.Api.Pushover/Ombi.Api.Pushover.csproj +++ b/src/Ombi.Api.Pushover/Ombi.Api.Pushover.csproj @@ -1,7 +1,7 @@ - + - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj b/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj index 07b8c609f..23cb45a6e 100644 --- a/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj +++ b/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 @@ -10,7 +10,7 @@ - + diff --git a/src/Ombi.Api.Service/Ombi.Api.Service.csproj b/src/Ombi.Api.Service/Ombi.Api.Service.csproj index 18ae372a2..11769e131 100644 --- a/src/Ombi.Api.Service/Ombi.Api.Service.csproj +++ b/src/Ombi.Api.Service/Ombi.Api.Service.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 @@ -12,7 +12,7 @@ - + diff --git a/src/Ombi.Api.SickRage/Ombi.Api.SickRage.csproj b/src/Ombi.Api.SickRage/Ombi.Api.SickRage.csproj index 809404bd4..bdd16edf8 100644 --- a/src/Ombi.Api.SickRage/Ombi.Api.SickRage.csproj +++ b/src/Ombi.Api.SickRage/Ombi.Api.SickRage.csproj @@ -1,7 +1,7 @@ - + - netstandard2.1 + net5.0 8.0 diff --git a/src/Ombi.Api.Slack/Ombi.Api.Slack.csproj b/src/Ombi.Api.Slack/Ombi.Api.Slack.csproj index 5c67fe577..f76114f83 100644 --- a/src/Ombi.Api.Slack/Ombi.Api.Slack.csproj +++ b/src/Ombi.Api.Slack/Ombi.Api.Slack.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.Sonarr/Ombi.Api.Sonarr.csproj b/src/Ombi.Api.Sonarr/Ombi.Api.Sonarr.csproj index 5c67fe577..f76114f83 100644 --- a/src/Ombi.Api.Sonarr/Ombi.Api.Sonarr.csproj +++ b/src/Ombi.Api.Sonarr/Ombi.Api.Sonarr.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.Telegram/Ombi.Api.Telegram.csproj b/src/Ombi.Api.Telegram/Ombi.Api.Telegram.csproj index 809404bd4..bdd16edf8 100644 --- a/src/Ombi.Api.Telegram/Ombi.Api.Telegram.csproj +++ b/src/Ombi.Api.Telegram/Ombi.Api.Telegram.csproj @@ -1,7 +1,7 @@ - + - netstandard2.1 + net5.0 8.0 diff --git a/src/Ombi.Api.Trakt/Ombi.Api.Trakt.csproj b/src/Ombi.Api.Trakt/Ombi.Api.Trakt.csproj index f7a2a3766..5cd35f0ac 100644 --- a/src/Ombi.Api.Trakt/Ombi.Api.Trakt.csproj +++ b/src/Ombi.Api.Trakt/Ombi.Api.Trakt.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.TvMaze/Ombi.Api.TvMaze.csproj b/src/Ombi.Api.TvMaze/Ombi.Api.TvMaze.csproj index 2bb0c6352..5457b0290 100644 --- a/src/Ombi.Api.TvMaze/Ombi.Api.TvMaze.csproj +++ b/src/Ombi.Api.TvMaze/Ombi.Api.TvMaze.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api.Twilio/Ombi.Api.Twilio.csproj b/src/Ombi.Api.Twilio/Ombi.Api.Twilio.csproj index dd350ff91..7a6146493 100644 --- a/src/Ombi.Api.Twilio/Ombi.Api.Twilio.csproj +++ b/src/Ombi.Api.Twilio/Ombi.Api.Twilio.csproj @@ -1,7 +1,7 @@ - + - netstandard2.1 + net5.0 diff --git a/src/Ombi.Api.Webhook/Ombi.Api.Webhook.csproj b/src/Ombi.Api.Webhook/Ombi.Api.Webhook.csproj index 84f215437..d258a95b2 100644 --- a/src/Ombi.Api.Webhook/Ombi.Api.Webhook.csproj +++ b/src/Ombi.Api.Webhook/Ombi.Api.Webhook.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Api/Ombi.Api.csproj b/src/Ombi.Api/Ombi.Api.csproj index a6af5851f..98da29c2a 100644 --- a/src/Ombi.Api/Ombi.Api.csproj +++ b/src/Ombi.Api/Ombi.Api.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 @@ -10,8 +10,8 @@ - - + + diff --git a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj index 2be1554de..e748264b4 100644 --- a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj +++ b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0 true true @@ -16,7 +16,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/Ombi.Core/Ombi.Core.csproj b/src/Ombi.Core/Ombi.Core.csproj index 4f987cea3..8acbaceb0 100644 --- a/src/Ombi.Core/Ombi.Core.csproj +++ b/src/Ombi.Core/Ombi.Core.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 @@ -12,10 +12,10 @@ - - + + - + diff --git a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index 9dfb38770..bbfe532eb 100644 --- a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 @@ -10,9 +10,9 @@ - + - + diff --git a/src/Ombi.HealthChecks/Ombi.HealthChecks.csproj b/src/Ombi.HealthChecks/Ombi.HealthChecks.csproj index 57c2ef059..5171c9c36 100644 --- a/src/Ombi.HealthChecks/Ombi.HealthChecks.csproj +++ b/src/Ombi.HealthChecks/Ombi.HealthChecks.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 diff --git a/src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj b/src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj index 7fd44f346..b0b8f58e7 100644 --- a/src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj +++ b/src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0 false @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Helpers/Ombi.Helpers.csproj b/src/Ombi.Helpers/Ombi.Helpers.csproj index 6b129146e..48969ac1a 100644 --- a/src/Ombi.Helpers/Ombi.Helpers.csproj +++ b/src/Ombi.Helpers/Ombi.Helpers.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 @@ -11,10 +11,10 @@ - - - - + + + + diff --git a/src/Ombi.Hubs/Ombi.Hubs.csproj b/src/Ombi.Hubs/Ombi.Hubs.csproj index ae2e158ad..1f994b552 100644 --- a/src/Ombi.Hubs/Ombi.Hubs.csproj +++ b/src/Ombi.Hubs/Ombi.Hubs.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 8.0 diff --git a/src/Ombi.Mapping/Ombi.Mapping.csproj b/src/Ombi.Mapping/Ombi.Mapping.csproj index 0b8645b08..cd0ee4724 100644 --- a/src/Ombi.Mapping/Ombi.Mapping.csproj +++ b/src/Ombi.Mapping/Ombi.Mapping.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Notifications.Templates/Ombi.Notifications.Templates.csproj b/src/Ombi.Notifications.Templates/Ombi.Notifications.Templates.csproj index 6a296fa24..35adcf7af 100644 --- a/src/Ombi.Notifications.Templates/Ombi.Notifications.Templates.csproj +++ b/src/Ombi.Notifications.Templates/Ombi.Notifications.Templates.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj index d73f6c745..9b48d1ad4 100644 --- a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj +++ b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj @@ -1,14 +1,14 @@  - netcoreapp3.1 + net5.0 - + diff --git a/src/Ombi.Notifications/Ombi.Notifications.csproj b/src/Ombi.Notifications/Ombi.Notifications.csproj index 93afe0c14..798026b47 100644 --- a/src/Ombi.Notifications/Ombi.Notifications.csproj +++ b/src/Ombi.Notifications/Ombi.Notifications.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj index 217d4997d..a2b89d5ad 100644 --- a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj +++ b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0 @@ -11,7 +11,7 @@ - + diff --git a/src/Ombi.Schedule/Jobs/Couchpotato/CouchPotatoSync.cs b/src/Ombi.Schedule/Jobs/Couchpotato/CouchPotatoSync.cs index d30091226..98a0c0910 100644 --- a/src/Ombi.Schedule/Jobs/Couchpotato/CouchPotatoSync.cs +++ b/src/Ombi.Schedule/Jobs/Couchpotato/CouchPotatoSync.cs @@ -80,7 +80,7 @@ namespace Ombi.Schedule.Jobs.Couchpotato // Let's remove the old cached data using (var tran = await _ctx.Database.BeginTransactionAsync()) { - await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM CouchPotatoCache"); + await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM CouchPotatoCache"); tran.Commit(); } diff --git a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAlbumSync.cs b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAlbumSync.cs index a8ed89b18..9530cf5a1 100644 --- a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAlbumSync.cs +++ b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAlbumSync.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; @@ -53,7 +54,7 @@ namespace Ombi.Schedule.Jobs.Lidarr // Let's remove the old cached data using (var tran = await _ctx.Database.BeginTransactionAsync()) { - await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM LidarrAlbumCache"); + await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM LidarrAlbumCache"); tran.Commit(); } diff --git a/src/Ombi.Schedule/Jobs/Lidarr/LidarrArtistSync.cs b/src/Ombi.Schedule/Jobs/Lidarr/LidarrArtistSync.cs index 47289f61a..edcd6cbe6 100644 --- a/src/Ombi.Schedule/Jobs/Lidarr/LidarrArtistSync.cs +++ b/src/Ombi.Schedule/Jobs/Lidarr/LidarrArtistSync.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Internal; using Microsoft.Extensions.Logging; using Ombi.Api.Lidarr; using Ombi.Core.Settings; @@ -53,7 +53,7 @@ namespace Ombi.Schedule.Jobs.Lidarr // Let's remove the old cached data using (var tran = await _ctx.Database.BeginTransactionAsync()) { - await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM LidarrArtistCache"); + await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM LidarrArtistCache"); tran.Commit(); } diff --git a/src/Ombi.Schedule/Jobs/SickRage/SickRageSync.cs b/src/Ombi.Schedule/Jobs/SickRage/SickRageSync.cs index 5956ee34b..a223a0cfa 100644 --- a/src/Ombi.Schedule/Jobs/SickRage/SickRageSync.cs +++ b/src/Ombi.Schedule/Jobs/SickRage/SickRageSync.cs @@ -48,7 +48,7 @@ namespace Ombi.Schedule.Jobs.SickRage var ids = srShows.Select(x => x.tvdbid); using (var tran = await _ctx.Database.BeginTransactionAsync()) { - await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM SickRageCache"); + await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM SickRageCache"); tran.Commit(); } @@ -57,7 +57,7 @@ namespace Ombi.Schedule.Jobs.SickRage await _ctx.SickRageCache.AddRangeAsync(entites); var episodesToAdd = new List(); - await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM SickRageEpisodeCache"); + await _ctx.Database.ExecuteSqlRawAsync("DELETE FROM SickRageEpisodeCache"); foreach (var s in srShows) { var seasons = await _api.GetSeasonList(s.tvdbid, settings.ApiKey, settings.FullUri); diff --git a/src/Ombi.Schedule/Ombi.Schedule.csproj b/src/Ombi.Schedule/Ombi.Schedule.csproj index 41d15d503..ac63d5b2f 100644 --- a/src/Ombi.Schedule/Ombi.Schedule.csproj +++ b/src/Ombi.Schedule/Ombi.Schedule.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Settings.Tests/Ombi.Settings.Tests.csproj b/src/Ombi.Settings.Tests/Ombi.Settings.Tests.csproj index a8674b9f2..50fb1cc00 100644 --- a/src/Ombi.Settings.Tests/Ombi.Settings.Tests.csproj +++ b/src/Ombi.Settings.Tests/Ombi.Settings.Tests.csproj @@ -1,6 +1,6 @@  - netcoreapp3.1 + net5.0 false @@ -8,7 +8,7 @@ - + diff --git a/src/Ombi.Settings/Ombi.Settings.csproj b/src/Ombi.Settings/Ombi.Settings.csproj index 8f2d80251..dc015ef9f 100644 --- a/src/Ombi.Settings/Ombi.Settings.csproj +++ b/src/Ombi.Settings/Ombi.Settings.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 @@ -10,9 +10,9 @@ - + - + diff --git a/src/Ombi.Store/Ombi.Store.csproj b/src/Ombi.Store/Ombi.Store.csproj index 9d6bc3e92..883819763 100644 --- a/src/Ombi.Store/Ombi.Store.csproj +++ b/src/Ombi.Store/Ombi.Store.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 3.0.0.0 3.0.0.0 @@ -11,15 +11,15 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - + diff --git a/src/Ombi.Test.Common/Ombi.Test.Common.csproj b/src/Ombi.Test.Common/Ombi.Test.Common.csproj index f8da0ff6d..d24488310 100644 --- a/src/Ombi.Test.Common/Ombi.Test.Common.csproj +++ b/src/Ombi.Test.Common/Ombi.Test.Common.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 8.0 diff --git a/src/Ombi.Tests/Ombi.Tests.csproj b/src/Ombi.Tests/Ombi.Tests.csproj index f4346de50..c589f4f74 100644 --- a/src/Ombi.Tests/Ombi.Tests.csproj +++ b/src/Ombi.Tests/Ombi.Tests.csproj @@ -1,19 +1,19 @@  - netcoreapp3.1 + net5.0 false - + - + diff --git a/src/Ombi.TheMovieDbApi/Ombi.Api.TheMovieDb.csproj b/src/Ombi.TheMovieDbApi/Ombi.Api.TheMovieDb.csproj index 2f8b0ce3b..d0611a8a9 100644 --- a/src/Ombi.TheMovieDbApi/Ombi.Api.TheMovieDb.csproj +++ b/src/Ombi.TheMovieDbApi/Ombi.Api.TheMovieDb.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + net5.0 Ombi.Api.TheMovieDb 3.0.0.0 3.0.0.0 diff --git a/src/Ombi.Updater/Ombi.Updater.csproj b/src/Ombi.Updater/Ombi.Updater.csproj index b23f3297a..7e959f6ec 100644 --- a/src/Ombi.Updater/Ombi.Updater.csproj +++ b/src/Ombi.Updater/Ombi.Updater.csproj @@ -3,7 +3,7 @@ Exe win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64; - netcoreapp3.1 + net5.0 3.0.0.0 3.0.0.0 @@ -12,14 +12,14 @@ - - - - - - - - + + + + + + + + diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts index c9b2f94fe..6d95241c5 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts @@ -136,7 +136,7 @@ export class MyNavComponent implements OnInit { if (hour >= 12 && hour < 18) { this.welcomeText = 'NavigationBar.AfternoonWelcome'; } - if (hour >= 18 && hour < 23) { + if (hour >= 18 && hour < 24) { this.welcomeText = 'NavigationBar.EveningWelcome'; } } diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index 7fd6ce5a9..7b27b8d96 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -1,6 +1,6 @@  - netcoreapp3.1 + net5.0 win10-x64;win10-x86;osx-x64;linux-x64;linux-arm;linux-arm64; false Latest @@ -62,23 +62,22 @@ - + - - + + - - - - + + + diff --git a/src/Ombi/databasea.json b/src/Ombi/databasea.json new file mode 100644 index 000000000..7c77dd61a --- /dev/null +++ b/src/Ombi/databasea.json @@ -0,0 +1,14 @@ +{ + "OmbiDatabase": { + "Type": "MySQL", + "ConnectionString": "Server=192.168.68.118;Database=app.ombi.io;User=ombi" + }, + "SettingsDatabase": { + "Type": "MySQL", + "ConnectionString": "Server=192.168.68.118;Database=app.ombi.io;User=ombi" + }, + "ExternalDatabase": { + "Type": "MySQL", + "ConnectionString": "Server=192.168.68.118;Database=app.ombi.io;User=ombi" + } + } \ No newline at end of file From 6b668c62d687f0c77e2a987990304fb0c476fbc8 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 10 Nov 2020 22:17:11 +0000 Subject: [PATCH 16/82] Fixed all warnings --- src/Ombi.Api.Plex/PlexApi.cs | 13 ++++++------- src/Ombi.Api/HttpRequestExtensions.cs | 6 +++--- .../Engine/Demo/DemoMovieSearchEngine.cs | 8 ++++---- .../Engine/Demo/DemoTvSearchEngine.cs | 2 +- src/Ombi.Core/Engine/V2/CalendarEngine.cs | 4 ++-- .../Agents/DiscordNotification.cs | 3 ++- .../Context/Sqlite/ExternalSqliteContext.cs | 2 ++ .../Requests/ITvRequestRepository.cs | 3 --- .../Controllers/V1/External/EmbyController.cs | 6 +----- src/Ombi/Controllers/V1/SettingsController.cs | 2 +- src/Ombi/Controllers/V1/TokenController.cs | 12 +++--------- src/Ombi/Extensions/StartupExtensions.cs | 2 +- src/Ombi/Ombi.csproj | 19 ------------------- 13 files changed, 26 insertions(+), 56 deletions(-) diff --git a/src/Ombi.Api.Plex/PlexApi.cs b/src/Ombi.Api.Plex/PlexApi.cs index eaafcd75f..0d6356457 100644 --- a/src/Ombi.Api.Plex/PlexApi.cs +++ b/src/Ombi.Api.Plex/PlexApi.cs @@ -135,16 +135,15 @@ namespace Ombi.Api.Plex } /// - // 192.168.1.69:32400/library/metadata/3662/allLeaves - // The metadata ratingkey should be in the Cache - // Search for it and then call the above with the Directory.RatingKey - // THEN! We need the episode metadata using result.Vide.Key ("/library/metadata/3664") - // We then have the GUID which contains the TVDB ID plus the season and episode number: guid="com.plexapp.agents.thetvdb://269586/2/8?lang=en" + /// 192.168.1.69:32400/library/metadata/3662/allLeaves + /// The metadata ratingkey should be in the Cache + /// Search for it and then call the above with the Directory.RatingKey + /// THEN! We need the episode metadata using result.Vide.Key ("/library/metadata/3664") + /// We then have the GUID which contains the TVDB ID plus the season and episode number: guid="com.plexapp.agents.thetvdb://269586/2/8?lang=en" /// /// /// /// - /// public async Task GetEpisodeMetaData(string authToken, string plexFullHost, int ratingKey) { var request = new Request($"/library/metadata/{ratingKey}", plexFullHost, HttpMethod.Get); @@ -308,7 +307,7 @@ namespace Ombi.Api.Plex } private async Task CheckInstallId(PlexSettings s) { - if (s.InstallId == null || s.InstallId == Guid.Empty) + if (s?.InstallId == Guid.Empty || s.InstallId == Guid.Empty) { s.InstallId = Guid.NewGuid(); await _plexSettings.SaveSettingsAsync(s); diff --git a/src/Ombi.Api/HttpRequestExtensions.cs b/src/Ombi.Api/HttpRequestExtensions.cs index fa2ded97d..a08180362 100644 --- a/src/Ombi.Api/HttpRequestExtensions.cs +++ b/src/Ombi.Api/HttpRequestExtensions.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; namespace Ombi.Api { - public static class HttpRequestExtnesions + public static class HttpRequestExtensions { public static async Task Clone(this HttpRequestMessage request) { @@ -14,9 +14,9 @@ namespace Ombi.Api Content = await request.Content.Clone(), Version = request.Version }; - foreach (KeyValuePair prop in request.Properties) + foreach (KeyValuePair prop in request.Options) { - clone.Properties.Add(prop); + clone.Options.TryAdd(prop.Key, prop.Value); } foreach (KeyValuePair> header in request.Headers) { diff --git a/src/Ombi.Core/Engine/Demo/DemoMovieSearchEngine.cs b/src/Ombi.Core/Engine/Demo/DemoMovieSearchEngine.cs index 86582fb4d..f392bf7ad 100644 --- a/src/Ombi.Core/Engine/Demo/DemoMovieSearchEngine.cs +++ b/src/Ombi.Core/Engine/Demo/DemoMovieSearchEngine.cs @@ -50,7 +50,7 @@ namespace Ombi.Core.Engine.Demo return null; } - public async Task> NowPlayingMovies() + new public async Task> NowPlayingMovies() { var rand = new Random(); var responses = new List(); @@ -72,18 +72,18 @@ namespace Ombi.Core.Engine.Demo return responses; } - public async Task> PopularMovies() + new public async Task> PopularMovies() { return await NowPlayingMovies(); } - public async Task> TopRatedMovies() + new public async Task> TopRatedMovies() { return await NowPlayingMovies(); } - public async Task> UpcomingMovies() + new public async Task> UpcomingMovies() { return await NowPlayingMovies(); diff --git a/src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs b/src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs index dd117fb53..343fb400a 100644 --- a/src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs +++ b/src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs @@ -35,7 +35,7 @@ namespace Ombi.Core.Engine.Demo private readonly DemoLists _demoLists; - public async Task> Search(string search) + new public async Task> Search(string search) { var searchResult = await TvMazeApi.Search(search); diff --git a/src/Ombi.Core/Engine/V2/CalendarEngine.cs b/src/Ombi.Core/Engine/V2/CalendarEngine.cs index 4b3618780..4a5814e1a 100644 --- a/src/Ombi.Core/Engine/V2/CalendarEngine.cs +++ b/src/Ombi.Core/Engine/V2/CalendarEngine.cs @@ -27,7 +27,7 @@ namespace Ombi.Core.Engine.V2 private readonly IMovieRequestRepository _movieRepo; private readonly ITvRequestRepository _tvRepo; - public async Task> GetCalendarData() + public Task> GetCalendarData() { var viewModel = new List(); var movies = _movieRepo.GetAll().Where(x => @@ -78,7 +78,7 @@ namespace Ombi.Core.Engine.V2 }); } - return viewModel; + return Task.FromResult(viewModel); } private string GetBackgroundColor(BaseRequest req) diff --git a/src/Ombi.Notifications/Agents/DiscordNotification.cs b/src/Ombi.Notifications/Agents/DiscordNotification.cs index 71d0e298c..dda10f210 100644 --- a/src/Ombi.Notifications/Agents/DiscordNotification.cs +++ b/src/Ombi.Notifications/Agents/DiscordNotification.cs @@ -144,7 +144,8 @@ namespace Ombi.Notifications.Agents "Available" => "51283", "Denied" => "13959168", "Processing Request" => "37354", - "Pending Approval" => "16754470" + "Pending Approval" => "16754470", + _ => throw new ArgumentOutOfRangeException(nameof(color)) }; } } diff --git a/src/Ombi.Store/Context/Sqlite/ExternalSqliteContext.cs b/src/Ombi.Store/Context/Sqlite/ExternalSqliteContext.cs index c85084d4a..f442531bc 100644 --- a/src/Ombi.Store/Context/Sqlite/ExternalSqliteContext.cs +++ b/src/Ombi.Store/Context/Sqlite/ExternalSqliteContext.cs @@ -27,7 +27,9 @@ namespace Ombi.Store.Context.Sqlite Database.ExecuteSqlRaw(@"INSERT OR IGNORE INTO __EFMigrationsHistory (MigrationId,ProductVersion) VALUES('20191103205133_Inital', '2.2.6-servicing-10079'); "); } +#pragma warning disable RCS1075 // Avoid empty catch clause that catches System.Exception. catch (Exception) +#pragma warning restore RCS1075 // Avoid empty catch clause that catches System.Exception. { // ignored } diff --git a/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs b/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs index 8c2c838e7..396ec9dfb 100644 --- a/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs +++ b/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs @@ -9,10 +9,7 @@ namespace Ombi.Store.Repository.Requests public interface ITvRequestRepository : IRepository { OmbiContext Db { get; } - Task Add(TvRequests request); Task AddChild(ChildRequests request); - Task Delete(TvRequests request); - Task DeleteRange(IEnumerable request); Task DeleteChild(ChildRequests request); IQueryable Get(); IQueryable GetLite(); diff --git a/src/Ombi/Controllers/V1/External/EmbyController.cs b/src/Ombi/Controllers/V1/External/EmbyController.cs index 4b201fe7c..29ad84eb2 100644 --- a/src/Ombi/Controllers/V1/External/EmbyController.cs +++ b/src/Ombi/Controllers/V1/External/EmbyController.cs @@ -19,11 +19,7 @@ namespace Ombi.Controllers.V1.External [Produces("application/json")] public class EmbyController : Controller { - /// - /// - /// - /// - /// + public EmbyController(IEmbyApiFactory emby, ISettingsService embySettings) { EmbyApi = emby; diff --git a/src/Ombi/Controllers/V1/SettingsController.cs b/src/Ombi/Controllers/V1/SettingsController.cs index 8cef872f4..1d340b4cd 100644 --- a/src/Ombi/Controllers/V1/SettingsController.cs +++ b/src/Ombi/Controllers/V1/SettingsController.cs @@ -183,7 +183,7 @@ namespace Ombi.Controllers.V1 [HttpPost("plex")] public async Task PlexSettings([FromBody]PlexSettings plex) { - if (plex.InstallId == null || plex.InstallId == Guid.Empty) + if (plex?.InstallId == Guid.Empty || plex.InstallId == Guid.Empty) { plex.InstallId = Guid.NewGuid(); } diff --git a/src/Ombi/Controllers/V1/TokenController.cs b/src/Ombi/Controllers/V1/TokenController.cs index ad6e8ca23..66013ecf5 100644 --- a/src/Ombi/Controllers/V1/TokenController.cs +++ b/src/Ombi/Controllers/V1/TokenController.cs @@ -135,7 +135,7 @@ namespace Ombi.Controllers.V1 new Claim("Id", user.Id) }; claims.AddRange(roles.Select(role => new Claim("role", role))); - if(user.Email.HasValue()) + if (user.Email.HasValue()) { claims.Add(new Claim("Email", user.Email)); } @@ -158,14 +158,8 @@ namespace Ombi.Controllers.V1 } user.LastLoggedIn = DateTime.UtcNow; - try - { - await _userManager.UpdateAsync(user); - } - catch (Exception) - { - - } + + await _userManager.UpdateAsync(user); return new JsonResult(new { diff --git a/src/Ombi/Extensions/StartupExtensions.cs b/src/Ombi/Extensions/StartupExtensions.cs index cc90155e7..d14dc2e9e 100644 --- a/src/Ombi/Extensions/StartupExtensions.cs +++ b/src/Ombi/Extensions/StartupExtensions.cs @@ -48,7 +48,7 @@ namespace Ombi Type = SecuritySchemeType.ApiKey }); c.CustomSchemaIds(x => x.FullName); - var basePath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); + var basePath = Path.GetDirectoryName(AppContext.BaseDirectory); var xmlPath = Path.Combine(basePath, "Swagger.xml"); try { diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index 7b27b8d96..6925e8903 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -20,22 +20,11 @@ false - - bin\Debug\netcoreapp2.2\Swagger.xml - 1701;1702;1705;1591; - - - - - bin\Release\netcoreapp2.2\Swagger.xml - 1701;1702;1705;1591; - TRACE;RELEASE;NETCOREAPP2_0 - @@ -72,7 +61,6 @@ - @@ -97,13 +85,6 @@ - - - Always - - - - From 056a7b48aa065fd92fa28b696629c66be209e088 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 10 Nov 2020 22:18:53 +0000 Subject: [PATCH 17/82] Applied single file publish --- .azuredevops/pipelines/templates/build-steps.yml | 6 +++--- .../pipelines/templates/publish-os-steps.yml | 12 ++++++------ .azuredevops/pipelines/templates/variables.yml | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.azuredevops/pipelines/templates/build-steps.yml b/.azuredevops/pipelines/templates/build-steps.yml index 86f7fc21c..111424222 100644 --- a/.azuredevops/pipelines/templates/build-steps.yml +++ b/.azuredevops/pipelines/templates/build-steps.yml @@ -5,7 +5,7 @@ steps: displayName: 'Use .NET Core sdk ' inputs: packageType: 'sdk' - version: '3.x' + version: '5.x' - task: Yarn@3 displayName: 'Install UI Dependancies' @@ -18,7 +18,7 @@ steps: inputs: projectDirectory: '$(UiLocation)' arguments: 'run build' - + - task: PublishPipelineArtifact@1 inputs: targetPath: '$(UiLocation)dist' @@ -31,4 +31,4 @@ steps: command: 'custom' projects: '$(TestProject)' custom: 'test' - continueOnError: false + continueOnError: false diff --git a/.azuredevops/pipelines/templates/publish-os-steps.yml b/.azuredevops/pipelines/templates/publish-os-steps.yml index ad72212cf..a90cf61ff 100644 --- a/.azuredevops/pipelines/templates/publish-os-steps.yml +++ b/.azuredevops/pipelines/templates/publish-os-steps.yml @@ -3,7 +3,7 @@ steps: displayName: 'Use .NET Core sdk ' inputs: packageType: 'sdk' - version: '3.x' + version: '5.x' - task: DotNetCoreInstaller@1 displayName: 'Use .NET Core sdk for versioning' @@ -24,23 +24,23 @@ steps: inputs: command: 'publish' publishWebProjects: true - arguments: '-c $(BuildConfiguration) -r "$(runtime)" -o $(Build.ArtifactStagingDirectory)/$(runtime)' + arguments: '-c $(BuildConfiguration) -r "$(runtime)" -o $(Build.ArtifactStagingDirectory)/$(runtime)' --self-contained true -p:PublishSingleFile=true zipAfterPublish: false modifyOutputPath: false - + - task: DownloadPipelineArtifact@2 inputs: buildType: 'current' artifactName: 'angular_dist' targetPath: '$(Build.ArtifactStagingDirectory)/angular_dist' - + - task: CopyFiles@2 displayName: 'Copy Angular App $(runtime)' inputs: SourceFolder: '$(Build.ArtifactStagingDirectory)/angular_dist' Contents: '**' TargetFolder: '$(Build.ArtifactStagingDirectory)/$(runtime)/ClientApp/dist' - + - task: ArchiveFiles@2 displayName: 'Zip $(runtime)' inputs: @@ -49,7 +49,7 @@ steps: archiveType: $(compression) archiveFile: '$(Build.ArtifactStagingDirectory)/$(runtime).$(format)' replaceExistingArchive: true - + - task: PublishPipelineArtifact@1 inputs: targetPath: '$(Build.ArtifactStagingDirectory)/$(runtime).$(format)' diff --git a/.azuredevops/pipelines/templates/variables.yml b/.azuredevops/pipelines/templates/variables.yml index 507746641..c33cd7999 100644 --- a/.azuredevops/pipelines/templates/variables.yml +++ b/.azuredevops/pipelines/templates/variables.yml @@ -12,7 +12,7 @@ variables: value: "**/*.Tests.csproj" - name: "NetCoreVersion" - value: "3.1" + value: "5.0" - name: "PublishLocation" value: "$(Build.SourcesDirectory)/src/Ombi/bin/Release/netcoreapp$(NetCoreVersion)" From 39cbfd9b0aaec31c82580af6a348e564bf00e900 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 10 Nov 2020 22:34:23 +0000 Subject: [PATCH 18/82] fix --- .azuredevops/pipelines/templates/publish-os-steps.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.azuredevops/pipelines/templates/publish-os-steps.yml b/.azuredevops/pipelines/templates/publish-os-steps.yml index a90cf61ff..b8614ea64 100644 --- a/.azuredevops/pipelines/templates/publish-os-steps.yml +++ b/.azuredevops/pipelines/templates/publish-os-steps.yml @@ -24,7 +24,7 @@ steps: inputs: command: 'publish' publishWebProjects: true - arguments: '-c $(BuildConfiguration) -r "$(runtime)" -o $(Build.ArtifactStagingDirectory)/$(runtime)' --self-contained true -p:PublishSingleFile=true + arguments: '-c $(BuildConfiguration) -r "$(runtime)" -o $(Build.ArtifactStagingDirectory)/$(runtime) --self-contained true -p:PublishSingleFile=true' zipAfterPublish: false modifyOutputPath: false From a26d232607b12699f851c3358d6d241b1a6ab065 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 10 Nov 2020 22:55:37 +0000 Subject: [PATCH 19/82] added bcl async --- src/Ombi.Core.Tests/Ombi.Core.Tests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj index e748264b4..c86ed0c94 100644 --- a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj +++ b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj @@ -8,6 +8,7 @@ + From 9fea842545f7c664d64bba0ba4d29ac19010d1c2 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 11 Nov 2020 08:14:42 +0000 Subject: [PATCH 20/82] wip --- src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj | 1 + src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj | 1 + src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj | 1 + src/Ombi.Settings.Tests/Ombi.Settings.Tests.csproj | 1 + src/Ombi.Tests/Ombi.Tests.csproj | 1 + 5 files changed, 5 insertions(+) diff --git a/src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj b/src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj index b0b8f58e7..1989f69d8 100644 --- a/src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj +++ b/src/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj index 9b48d1ad4..469611ac8 100644 --- a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj +++ b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj index a2b89d5ad..f38ba1d92 100644 --- a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj +++ b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj @@ -9,6 +9,7 @@ + diff --git a/src/Ombi.Settings.Tests/Ombi.Settings.Tests.csproj b/src/Ombi.Settings.Tests/Ombi.Settings.Tests.csproj index 50fb1cc00..4e86eb9fe 100644 --- a/src/Ombi.Settings.Tests/Ombi.Settings.Tests.csproj +++ b/src/Ombi.Settings.Tests/Ombi.Settings.Tests.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Ombi.Tests/Ombi.Tests.csproj b/src/Ombi.Tests/Ombi.Tests.csproj index c589f4f74..97970530e 100644 --- a/src/Ombi.Tests/Ombi.Tests.csproj +++ b/src/Ombi.Tests/Ombi.Tests.csproj @@ -12,6 +12,7 @@ + From de80558f54d8065d0bcc2e6e3eb27a2670a1b37b Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 11 Nov 2020 09:21:27 +0000 Subject: [PATCH 21/82] fixed unit tests --- src/Ombi.Core/Models/Search/V2/MovieCollectionsViewModel.cs | 1 - src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj | 2 +- src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs | 3 ++- src/Ombi.Test.Common/Ombi.Test.Common.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ombi.Core/Models/Search/V2/MovieCollectionsViewModel.cs b/src/Ombi.Core/Models/Search/V2/MovieCollectionsViewModel.cs index ace5ce76c..47870c18b 100644 --- a/src/Ombi.Core/Models/Search/V2/MovieCollectionsViewModel.cs +++ b/src/Ombi.Core/Models/Search/V2/MovieCollectionsViewModel.cs @@ -13,7 +13,6 @@ namespace Ombi.Core.Models.Search.V2 public class MovieCollection : SearchViewModel { - public int Id { get; set; } public string Overview { get; set; } public string PosterPath { get; set; } public string Title { get; set; } diff --git a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj index f38ba1d92..003c240f1 100644 --- a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj +++ b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index 32e2dd1da..d1fdd0788 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -16,7 +16,6 @@ using Ombi.Api.CouchPotato.Models; using Ombi.Api.Lidarr; using Ombi.Api.Lidarr.Models; using Ombi.Api.TheMovieDb; -using Ombi.Api.TheMovieDb; using Ombi.Api.TheMovieDb.Models; using Ombi.Api.TvMaze; using Ombi.Core.Settings; @@ -649,7 +648,9 @@ namespace Ombi.Schedule.Jobs.Ombi { releaseDate = $"({DateTime.Parse(info.ReleaseDate).Year})"; } +#pragma warning disable RCS1075 // Avoid empty catch clause that catches System.Exception. catch (Exception) +#pragma warning restore RCS1075 // Avoid empty catch clause that catches System.Exception. { // Swallow, couldn't parse the date } diff --git a/src/Ombi.Test.Common/Ombi.Test.Common.csproj b/src/Ombi.Test.Common/Ombi.Test.Common.csproj index d24488310..ecc1edfd4 100644 --- a/src/Ombi.Test.Common/Ombi.Test.Common.csproj +++ b/src/Ombi.Test.Common/Ombi.Test.Common.csproj @@ -8,7 +8,7 @@ - + From 33d4d600ac590eb8de19d788a422b1f47bbe975d Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 11 Nov 2020 10:48:26 +0000 Subject: [PATCH 22/82] install .net core 3.1 for the versioning package --- .azuredevops/pipelines/templates/publish-os-steps.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.azuredevops/pipelines/templates/publish-os-steps.yml b/.azuredevops/pipelines/templates/publish-os-steps.yml index b8614ea64..2f63fa4a4 100644 --- a/.azuredevops/pipelines/templates/publish-os-steps.yml +++ b/.azuredevops/pipelines/templates/publish-os-steps.yml @@ -9,7 +9,7 @@ steps: displayName: 'Use .NET Core sdk for versioning' inputs: packageType: 'sdk' - version: '2.1.x' + version: '3.1.x' - task: PowerShell@2 displayName: 'Set Version' From 7e81830307f9a136b0760c7f5607e8a523264c25 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 11 Nov 2020 16:36:47 +0000 Subject: [PATCH 23/82] Fixed the MySQL issue --- src/Ombi.Store/Ombi.Store.csproj | 2 +- src/Ombi/Ombi.csproj | 2 +- src/Ombi/databasea.json | 14 -------------- 3 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 src/Ombi/databasea.json diff --git a/src/Ombi.Store/Ombi.Store.csproj b/src/Ombi.Store/Ombi.Store.csproj index 883819763..78fa5b8c6 100644 --- a/src/Ombi.Store/Ombi.Store.csproj +++ b/src/Ombi.Store/Ombi.Store.csproj @@ -18,7 +18,7 @@ - + diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index 6925e8903..04a896596 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -57,7 +57,7 @@ - + diff --git a/src/Ombi/databasea.json b/src/Ombi/databasea.json deleted file mode 100644 index 7c77dd61a..000000000 --- a/src/Ombi/databasea.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "OmbiDatabase": { - "Type": "MySQL", - "ConnectionString": "Server=192.168.68.118;Database=app.ombi.io;User=ombi" - }, - "SettingsDatabase": { - "Type": "MySQL", - "ConnectionString": "Server=192.168.68.118;Database=app.ombi.io;User=ombi" - }, - "ExternalDatabase": { - "Type": "MySQL", - "ConnectionString": "Server=192.168.68.118;Database=app.ombi.io;User=ombi" - } - } \ No newline at end of file From a6cce3362e41ac5dd3d62378b0ee4c8e89c7f144 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 17 Nov 2020 14:45:26 +0000 Subject: [PATCH 24/82] Fixed the gravatar image #3864 --- BuildTask.ps1 | 12 - build.cake | 306 ------------------ build.ps1 | 242 -------------- build.sh | 101 ------ src/Ombi/ClientApp/src/app/app.component.html | 2 +- src/Ombi/ClientApp/src/app/app.component.ts | 35 +- .../src/app/my-nav/my-nav.component.html | 2 +- .../src/app/my-nav/my-nav.component.ts | 1 + 8 files changed, 18 insertions(+), 683 deletions(-) delete mode 100644 BuildTask.ps1 delete mode 100644 build.cake delete mode 100644 build.ps1 delete mode 100755 build.sh diff --git a/BuildTask.ps1 b/BuildTask.ps1 deleted file mode 100644 index 9ed055206..000000000 --- a/BuildTask.ps1 +++ /dev/null @@ -1,12 +0,0 @@ - -param([String]$env='local') - -"Environment: " + $env | Write-Output; -"Build Version: " + $env:APPVEYOR_BUILD_VERSION | Write-Output; -"Base Path: " + $env:APPVEYOR_BUILD_FOLDER | Write-Output; - -$appSettingsPath = $env:APPVEYOR_BUILD_FOLDER + '\src\Ombi\appsettings.json' -$appSettings = Get-Content $appSettingsPath -raw -$appSettings = $appSettings.Replace("{{VERSIONNUMBER}}",$env:APPVEYOR_BUILD_VERSION); -$appSettings = $appSettings.Replace("{{BRANCH}}",$env:APPVEYOR_REPO_BRANCH); -Set-Content -Path $appSettingsPath -Value $appSettings diff --git a/build.cake b/build.cake deleted file mode 100644 index 49d446de1..000000000 --- a/build.cake +++ /dev/null @@ -1,306 +0,0 @@ - -#tool "nuget:?package=GitVersion.CommandLine&version=5.0.1" -#addin nuget:?package=SharpZipLib&version=1.2.0 -#addin nuget:?package=Cake.Compression&version=0.2.3 -#addin "Cake.Incubator&version=5.1.0" -#addin nuget:?package=Cake.Yarn&version=0.4.6 - -////////////////////////////////////////////////////////////////////// -// ARGUMENTS -////////////////////////////////////////////////////////////////////// - -var target = Argument("target", "Default"); -var configuration = Argument("configuration", "Release"); - -////////////////////////////////////////////////////////////////////// -// PREPARATION -////////////////////////////////////////////////////////////////////// - -var buildDir = "./src/Ombi/bin/" + configuration; -var nodeModulesDir ="./src/Ombi/ClientApp/node_modules/"; -var wwwRootDistDir = "./src/Ombi/wwwroot/dist/"; -var projDir = "./src/"; // Project Directory -var webProjDir = "./src/Ombi"; -var uiProjectDir = "./src/Ombi/ClientApp"; -var csProj = "./src/Ombi/Ombi.csproj"; // Path to the project.csproj -var solutionFile = "Ombi.sln"; // Solution file if needed -GitVersion versionInfo = null; - -var frameworkVer = "netcoreapp3.0"; - -var buildSettings = new DotNetCoreBuildSettings -{ - Framework = frameworkVer, - Configuration = "Release", - OutputDirectory = Directory(buildDir), -}; - -var publishSettings = new DotNetCorePublishSettings -{ - Framework = frameworkVer, - Configuration = "Release", - OutputDirectory = Directory(buildDir), -}; - -var artifactsFolder = buildDir + "/"+frameworkVer+"/"; -var windowsArtifactsFolder = artifactsFolder + "win10-x64/published"; -var windows32BitArtifactsFolder = artifactsFolder + "win10-x86/published"; -var osxArtifactsFolder = artifactsFolder + "osx-x64/published"; -var linuxArtifactsFolder = artifactsFolder + "linux-x64/published"; -var linuxArmArtifactsFolder = artifactsFolder + "linux-arm/published"; -var linuxArm64BitArtifactsFolder = artifactsFolder + "linux-arm64/published"; - - - - -////////////////////////////////////////////////////////////////////// -// TASKS -////////////////////////////////////////////////////////////////////// - -Task("Clean") - .Does(() => -{ - CleanDirectory(buildDir); - //CleanDirectory(nodeModulesDir); - CleanDirectory(wwwRootDistDir); -}); - -Task("SetVersionInfo") - .IsDependentOn("Clean") - .Does(() => -{ - var settings = new GitVersionSettings { - RepositoryPath = ".", - }; - - if (AppVeyor.IsRunningOnAppVeyor) { - settings.Branch = AppVeyor.Environment.Repository.Branch; - } else { - settings.Branch = "master"; - } - - versionInfo = GitVersion(settings); - -// Information("GitResults -> {0}", versionInfo.Dump()); - -//Information(@"Build:{0}",AppVeyor.Environment.Build.Dump()); - - var buildVersion = string.Empty; - if(string.IsNullOrEmpty(AppVeyor.Environment.Build.Version)) - { - buildVersion = "3.0.000"; - } else{ - buildVersion = AppVeyor.Environment.Build.Version; - } - - if(versionInfo.BranchName.Contains("_")) - { - versionInfo.BranchName = versionInfo.BranchName.Replace("_","-"); - } - var fullVer = buildVersion + "-" + versionInfo.BranchName; - - if(versionInfo.PreReleaseTag.Contains("PullRequest")) - { - fullVer = buildVersion + "-PR"; - } - if(fullVer.Contains("_")) - { - fullVer = fullVer.Replace("_",""); - } - if(fullVer.Contains("/")) - { - fullVer = fullVer.Replace("/",""); - } - - buildSettings.ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.AssemblySemVer); - buildSettings.ArgumentCustomization = args => args.Append("/p:FullVer=" + fullVer); - publishSettings.ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.AssemblySemVer); - publishSettings.ArgumentCustomization = args => args.Append("/p:FullVer=" + fullVer); - //buildSettings.VersionSuffix = versionInfo.BranchName; - //publishSettings.VersionSuffix = versionInfo.BranchName; -}); - -Task("NPM") - .Does(() => { - Yarn.FromPath(uiProjectDir).Install(); -}); - -Task("Gulp Publish") - .IsDependentOn("NPM") - .Does(() => { - Yarn.FromPath(uiProjectDir).RunScript("build"); - }); - -Task("TSLint") - .Does(() => -{ - //Yarn.FromPath(uiProjectDir).RunScript("lint"); -}); - -Task("PrePublish") - .IsDependentOn("SetVersionInfo"); - //.IsDependentOn("Gulp Publish") // these are done in the main csproj - //.IsDependentOn("TSLint"); - - -Task("Package") - .Does(() => -{ - Zip(windowsArtifactsFolder +"/",artifactsFolder + "windows.zip"); - Zip(windows32BitArtifactsFolder +"/",artifactsFolder + "windows-32bit.zip"); - GZipCompress(osxArtifactsFolder, artifactsFolder + "osx.tar.gz"); - GZipCompress(linuxArtifactsFolder, artifactsFolder + "linux.tar.gz"); - GZipCompress(linuxArmArtifactsFolder, artifactsFolder + "linux-arm.tar.gz"); - GZipCompress(linuxArm64BitArtifactsFolder, artifactsFolder + "linux-arm64.tar.gz"); -}); - -Task("Publish") - .IsDependentOn("Upload-Test-Results") - .IsDependentOn("PrePublish") - .IsDependentOn("Publish-Windows") - .IsDependentOn("Publish-Windows-32bit") - .IsDependentOn("Publish-OSX") - .IsDependentOn("Publish-Linux") - .IsDependentOn("Publish-Linux-ARM") - .IsDependentOn("Publish-Linux-ARM-64Bit") - .IsDependentOn("Package"); - -Task("Publish-Windows") - .Does(() => -{ - publishSettings.Runtime = "win10-x64"; - publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/win10-x64/published"); - - DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings); - CopyFile(buildDir + "/"+frameworkVer+"/win10-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/win10-x64/published/Swagger.xml"); - - publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/win10-x64/published/updater"); - DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); -}); - -Task("Publish-Windows-32bit") - .Does(() => -{ - publishSettings.Runtime = "win10-x86"; - publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer+"/win10-x86/published"); - - DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings); - CopyFile(buildDir + "/"+frameworkVer+"/win10-x86/Swagger.xml", buildDir + "/"+frameworkVer+"/win10-x86/published/Swagger.xml"); - - - publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/win10-x86/published/updater"); - DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); -}); - -Task("Publish-OSX") - .Does(() => -{ - publishSettings.Runtime = "osx-x64"; - publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer+"/osx-x64/published"); - - DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings); - CopyFile(buildDir + "/"+frameworkVer+"/osx-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/osx-x64/published/Swagger.xml"); - - publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/osx-x64/published/updater"); - DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); -}); - -Task("Publish-Linux") - .Does(() => -{ - publishSettings.Runtime = "linux-x64"; - publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer+"/linux-x64/published"); - - DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings); - CopyFile(buildDir + "/"+frameworkVer+"/linux-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/linux-x64/published/Swagger.xml"); - - publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/linux-x64/published/updater"); - DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); -}); - -Task("Publish-Linux-ARM") - .Does(() => -{ - publishSettings.Runtime = "linux-arm"; - publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer+"/linux-arm/published"); - - DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings); - CopyFile( - buildDir + "/"+frameworkVer+"/linux-arm/Swagger.xml", - buildDir + "/"+frameworkVer+"/linux-arm/published/Swagger.xml"); - - publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/linux-arm/published/updater"); - DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); -}); - -Task("Publish-Linux-ARM-64Bit") - .Does(() => -{ - publishSettings.Runtime = "linux-arm64"; - publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer+"/linux-arm64/published"); - - DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings); - CopyFile( - buildDir + "/"+frameworkVer+"/linux-arm64/Swagger.xml", - buildDir + "/"+frameworkVer+"/linux-arm64/published/Swagger.xml"); - - publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/linux-arm64/published/updater"); - DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings); -}); - -Task("Run-Unit-Tests") - .Does(() => -{ - var settings = new DotNetCoreTestSettings - { - ArgumentCustomization = args => args.Append("--logger \"trx;LogFileName=Test.trx\""), - Configuration = "Release" - }; - var projectFiles = GetFiles("./**/*Tests.csproj"); - foreach(var file in projectFiles) - { - DotNetCoreTest(file.FullPath, settings); - } - - -}); - -Task("Upload-Test-Results") - .IsDependentOn("Run-Unit-Tests") - .ContinueOnError() - .Does(() => { - - }); - -Task("Run-Server-Build") - .Does(() => - { - var settings = new DotNetCoreBuildSettings - { - Framework = frameworkVer, - Configuration = "Release", - OutputDirectory = Directory(buildDir) - }; - DotNetCoreBuild(csProj, settings); - }); - -Task("Run-UI-Build") - .IsDependentOn("PrePublish"); -////////////////////////////////////////////////////////////////////// -// TASK TARGETS -////////////////////////////////////////////////////////////////////// - -Task("Default") - .IsDependentOn("Publish"); - -Task("Build") - .IsDependentOn("SetVersionInfo") - .IsDependentOn("Upload-Test-Results") - .IsDependentOn("Run-Server-Build"); - // .IsDependentOn("Run-UI-Build"); - -////////////////////////////////////////////////////////////////////// -// EXECUTION -////////////////////////////////////////////////////////////////////// - -RunTarget(target); diff --git a/build.ps1 b/build.ps1 deleted file mode 100644 index e61281292..000000000 --- a/build.ps1 +++ /dev/null @@ -1,242 +0,0 @@ -########################################################################## -# This is the Cake bootstrapper script for PowerShell. -# This file was downloaded from https://github.com/cake-build/resources -# Feel free to change this file to fit your needs. -########################################################################## - -<# - -.SYNOPSIS -This is a Powershell script to bootstrap a Cake build. - -.DESCRIPTION -This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) -and execute your Cake build script with the parameters you provide. - -.PARAMETER Script -The build script to execute. -.PARAMETER Target -The build script target to run. -.PARAMETER Configuration -The build configuration to use. -.PARAMETER Verbosity -Specifies the amount of information to be displayed. -.PARAMETER ShowDescription -Shows description about tasks. -.PARAMETER DryRun -Performs a dry run. -.PARAMETER SkipToolPackageRestore -Skips restoring of packages. -.PARAMETER ScriptArgs -Remaining arguments are added here. - -.LINK -https://cakebuild.net - -#> - -[CmdletBinding()] -Param( - [string]$Script = "build.cake", - [string]$Target, - [string]$Configuration, - [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] - [string]$Verbosity, - [switch]$ShowDescription, - [Alias("WhatIf", "Noop")] - [switch]$DryRun, - [switch]$SkipToolPackageRestore, - [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] - [string[]]$ScriptArgs -) - -# Attempt to set highest encryption available for SecurityProtocol. -# PowerShell will not set this by default (until maybe .NET 4.6.x). This -# will typically produce a message for PowerShell v2 (just an info -# message though) -try { - # Set TLS 1.2 (3072), then TLS 1.1 (768), then TLS 1.0 (192), finally SSL 3.0 (48) - # Use integers because the enumeration values for TLS 1.2 and TLS 1.1 won't - # exist in .NET 4.0, even though they are addressable if .NET 4.5+ is - # installed (.NET 4.5 is an in-place upgrade). - [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48 - } catch { - Write-Output 'Unable to set PowerShell to use TLS 1.2 and TLS 1.1 due to old .NET Framework installed. If you see underlying connection closed or trust errors, you may need to upgrade to .NET Framework 4.5+ and PowerShell v3' - } - -[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null -function MD5HashFile([string] $filePath) -{ - if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf)) - { - return $null - } - - [System.IO.Stream] $file = $null; - [System.Security.Cryptography.MD5] $md5 = $null; - try - { - $md5 = [System.Security.Cryptography.MD5]::Create() - $file = [System.IO.File]::OpenRead($filePath) - return [System.BitConverter]::ToString($md5.ComputeHash($file)) - } - finally - { - if ($file -ne $null) - { - $file.Dispose() - } - } -} - -function GetProxyEnabledWebClient -{ - $wc = New-Object System.Net.WebClient - $proxy = [System.Net.WebRequest]::GetSystemWebProxy() - $proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials - $wc.Proxy = $proxy - return $wc -} - -Write-Host "Preparing to run build script..." - -if(!$PSScriptRoot){ - $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent -} - -$TOOLS_DIR = Join-Path $PSScriptRoot "tools" -$ADDINS_DIR = Join-Path $TOOLS_DIR "Addins" -$MODULES_DIR = Join-Path $TOOLS_DIR "Modules" -$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" -$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" -$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" -$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum" -$ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config" -$MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config" - -# Make sure tools folder exists -if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { - Write-Verbose -Message "Creating tools directory..." - New-Item -Path $TOOLS_DIR -Type directory | out-null -} - -# Make sure that packages.config exist. -if (!(Test-Path $PACKAGES_CONFIG)) { - Write-Verbose -Message "Downloading packages.config..." - try { - $wc = GetProxyEnabledWebClient - $wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) - } catch { - Throw "Could not download packages.config." - } -} - -# Try find NuGet.exe in path if not exists -if (!(Test-Path $NUGET_EXE)) { - Write-Verbose -Message "Trying to find nuget.exe in PATH..." - $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) } - $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 - if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { - Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)." - $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName - } -} - -# Try download NuGet.exe if not exists -if (!(Test-Path $NUGET_EXE)) { - Write-Verbose -Message "Downloading NuGet.exe..." - try { - $wc = GetProxyEnabledWebClient - $wc.DownloadFile($NUGET_URL, $NUGET_EXE) - } catch { - Throw "Could not download NuGet.exe." - } -} - -# Save nuget.exe path to environment to be available to child processed -$ENV:NUGET_EXE = $NUGET_EXE - -# Restore tools from NuGet? -if(-Not $SkipToolPackageRestore.IsPresent) { - Push-Location - Set-Location $TOOLS_DIR - - # Check for changes in packages.config and remove installed tools if true. - [string] $md5Hash = MD5HashFile($PACKAGES_CONFIG) - if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or - ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) { - Write-Verbose -Message "Missing or changed package.config hash..." - Get-ChildItem -Exclude packages.config,nuget.exe,Cake.Bakery | - Remove-Item -Recurse - } - - Write-Verbose -Message "Restoring tools from NuGet..." - $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" - - if ($LASTEXITCODE -ne 0) { - Throw "An error occurred while restoring NuGet tools." - } - else - { - $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" - } - Write-Verbose -Message ($NuGetOutput | out-string) - - Pop-Location -} - -# Restore addins from NuGet -if (Test-Path $ADDINS_PACKAGES_CONFIG) { - Push-Location - Set-Location $ADDINS_DIR - - Write-Verbose -Message "Restoring addins from NuGet..." - $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`"" - - if ($LASTEXITCODE -ne 0) { - Throw "An error occurred while restoring NuGet addins." - } - - Write-Verbose -Message ($NuGetOutput | out-string) - - Pop-Location -} - -# Restore modules from NuGet -if (Test-Path $MODULES_PACKAGES_CONFIG) { - Push-Location - Set-Location $MODULES_DIR - - Write-Verbose -Message "Restoring modules from NuGet..." - $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`"" - - if ($LASTEXITCODE -ne 0) { - Throw "An error occurred while restoring NuGet modules." - } - - Write-Verbose -Message ($NuGetOutput | out-string) - - Pop-Location -} - -# Make sure that Cake has been installed. -if (!(Test-Path $CAKE_EXE)) { - Throw "Could not find Cake.exe at $CAKE_EXE" -} - - - -# Build Cake arguments -$cakeArguments = @("$Script"); -if ($Target) { $cakeArguments += "-target=$Target" } -if ($Configuration) { $cakeArguments += "-configuration=$Configuration" } -if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" } -if ($ShowDescription) { $cakeArguments += "-showdescription" } -if ($DryRun) { $cakeArguments += "-dryrun" } -$cakeArguments += $ScriptArgs - -# Start Cake -Write-Host "Running build script..." -&$CAKE_EXE $cakeArguments -exit $LASTEXITCODE \ No newline at end of file diff --git a/build.sh b/build.sh deleted file mode 100755 index 6e14489c8..000000000 --- a/build.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env bash - -########################################################################## -# This is the Cake bootstrapper script for Linux and OS X. -# This file was downloaded from https://github.com/cake-build/resources -# Feel free to change this file to fit your needs. -########################################################################## - -# Define directories. -SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) -TOOLS_DIR=$SCRIPT_DIR/tools -NUGET_EXE=$TOOLS_DIR/nuget.exe -CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe -PACKAGES_CONFIG=$TOOLS_DIR/packages.config -PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum - -# Define md5sum or md5 depending on Linux/OSX -MD5_EXE= -if [[ "$(uname -s)" == "Darwin" ]]; then - MD5_EXE="md5 -r" -else - MD5_EXE="md5sum" -fi - -# Define default arguments. -SCRIPT="build.cake" -TARGET="Default" -CONFIGURATION="Release" -VERBOSITY="verbose" -DRYRUN= -SHOW_VERSION=false -SCRIPT_ARGUMENTS=() - -# Parse arguments. -for i in "$@"; do - case $1 in - -s|--script) SCRIPT="$2"; shift ;; - -t|--target) TARGET="$2"; shift ;; - -c|--configuration) CONFIGURATION="$2"; shift ;; - -v|--verbosity) VERBOSITY="$2"; shift ;; - -d|--dryrun) DRYRUN="-dryrun" ;; - --version) SHOW_VERSION=true ;; - --) shift; SCRIPT_ARGUMENTS+=("$@"); break ;; - *) SCRIPT_ARGUMENTS+=("$1") ;; - esac - shift -done - -# Make sure the tools folder exist. -if [ ! -d "$TOOLS_DIR" ]; then - mkdir "$TOOLS_DIR" -fi - -# Make sure that packages.config exist. -if [ ! -f "$TOOLS_DIR/packages.config" ]; then - echo "Downloading packages.config..." - curl -Lsfo "$TOOLS_DIR/packages.config" https://cakebuild.net/download/bootstrapper/packages - if [ $? -ne 0 ]; then - echo "An error occurred while downloading packages.config." - exit 1 - fi -fi - -# Download NuGet if it does not exist. -if [ ! -f "$NUGET_EXE" ]; then - echo "Downloading NuGet..." - curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe - if [ $? -ne 0 ]; then - echo "An error occurred while downloading nuget.exe." - exit 1 - fi -fi - -# Restore tools from NuGet. -pushd "$TOOLS_DIR" >/dev/null -if [ ! -f $PACKAGES_CONFIG_MD5 ] || [ "$( cat $PACKAGES_CONFIG_MD5 | sed 's/\r$//' )" != "$( $MD5_EXE $PACKAGES_CONFIG | awk '{ print $1 }' )" ]; then - find . -type d ! -name . | xargs rm -rf -fi - -mono "$NUGET_EXE" install -ExcludeVersion -if [ $? -ne 0 ]; then - echo "Could not restore NuGet packages." - exit 1 -fi - -$MD5_EXE $PACKAGES_CONFIG | awk '{ print $1 }' >| $PACKAGES_CONFIG_MD5 - -popd >/dev/null - -# Make sure that Cake has been installed. -if [ ! -f "$CAKE_EXE" ]; then - echo "Could not find Cake.exe at '$CAKE_EXE'." - exit 1 -fi - -# Start Cake -if $SHOW_VERSION; then - exec mono "$CAKE_EXE" -version -else - exec mono "$CAKE_EXE" $SCRIPT -verbosity=$VERBOSITY -configuration=$CONFIGURATION -target=$TARGET $DRYRUN "${SCRIPT_ARGUMENTS[@]}" -fi diff --git a/src/Ombi/ClientApp/src/app/app.component.html b/src/Ombi/ClientApp/src/app/app.component.html index c9f18a210..d4297aeb4 100644 --- a/src/Ombi/ClientApp/src/app/app.component.html +++ b/src/Ombi/ClientApp/src/app/app.component.html @@ -170,7 +170,7 @@
- + diff --git a/src/Ombi/ClientApp/src/app/app.component.ts b/src/Ombi/ClientApp/src/app/app.component.ts index 6324f60d7..f6f9fe62a 100644 --- a/src/Ombi/ClientApp/src/app/app.component.ts +++ b/src/Ombi/ClientApp/src/app/app.component.ts @@ -55,6 +55,18 @@ export class AppComponent implements OnInit { this.translate.addLangs(["en", "de", "fr", "da", "es", "it", "nl", "sk", "sv", "no", "pl", "pt"]); if (this.authService.loggedIn()) { + this.user = this.authService.claims(); + this.username = this.user.name; + if (!this.hubConnected) { + this.signalrNotification.initialize(); + this.hubConnected = true; + + this.signalrNotification.Notification.subscribe(data => { + this.snackBar.open(data, "OK", { + duration: 3000 + }); + }); + } this.identity.getUser().subscribe(u => { if (u.language) { this.translate.use(u.language); @@ -105,28 +117,11 @@ export class AppComponent implements OnInit { this.router.events.subscribe((event: NavigationStart) => { this.currentUrl = event.url; if (event instanceof NavigationStart) { - this.user = this.authService.claims(); - this.username = this.user.name; - this.isAdmin = this.authService.hasRole("admin"); this.showNav = this.authService.loggedIn(); - - if (this.authService.loggedIn()) { - - if (!this.isAdmin) { - // let's get the remaining requests etc - } - - if (!this.hubConnected) { - this.signalrNotification.initialize(); - this.hubConnected = true; - - this.signalrNotification.Notification.subscribe(data => { - this.snackBar.open(data, "OK", { - duration: 3000 - }); - }); - } + if (this.showNav) { + this.user = this.authService.claims(); + this.username = this.user.name; } } }); diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html index 1e44c367b..849fc9f21 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html @@ -9,7 +9,7 @@
+ src="https://www.gravatar.com/avatar/{{emailHash}}?d={{applicationLogo ? applicationLogo : 'https://raw.githubusercontent.com/tidusjar/Ombi/gh-pages/img/android-chrome-512x512.png'}}" />

{{username}}

diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts index 6d95241c5..553dcbf2c 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts @@ -31,6 +31,7 @@ export class MyNavComponent implements OnInit { @Input() public showNav: boolean; @Input() public applicationName: string; + @Input() public applicationLogo: string; @Input() public username: string; @Input() public isAdmin: string; @Input() public email: string; From 294b075b4e0c0eec5693cc5a9af62e6f7cd65004 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Fri, 20 Nov 2020 06:20:02 +0000 Subject: [PATCH 25/82] fix: src/Ombi/ClientApp/package.json & src/Ombi/ClientApp/yarn.lock to reduce vulnerabilities The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-Y18N-1021887 --- src/Ombi/ClientApp/package.json | 2 +- src/Ombi/ClientApp/yarn.lock | 58 +++++++++++++++++++++++++++++---- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/Ombi/ClientApp/package.json b/src/Ombi/ClientApp/package.json index 388815c78..988d20daf 100644 --- a/src/Ombi/ClientApp/package.json +++ b/src/Ombi/ClientApp/package.json @@ -15,7 +15,7 @@ "@angular/compiler": "^9.1.7", "@angular/core": "^9.1.7", "@angular/forms": "^9.1.7", - "@angular/localize": "^9.1.7", + "@angular/localize": "^11.0.2", "@angular/material": "^9.2.3", "@angular/platform-browser": "^9.1.7", "@angular/platform-browser-dynamic": "^9.1.7", diff --git a/src/Ombi/ClientApp/yarn.lock b/src/Ombi/ClientApp/yarn.lock index 113ad95c3..d2dc99e84 100644 --- a/src/Ombi/ClientApp/yarn.lock +++ b/src/Ombi/ClientApp/yarn.lock @@ -203,14 +203,14 @@ resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-9.1.8.tgz#dcc8d27c355876ba0a6f4ea4375703e45b6d994c" integrity sha512-+G2uFUNLCUEn4oThPO1reMHW1RzFqqJpvVs2joKnTzpAwOXWcPppj0vvmX/WzbZ7HWouFg8fjNfRn5tBVMgyXg== -"@angular/localize@^9.1.7": - version "9.1.8" - resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-9.1.8.tgz#7887b59358169e3fa1667e26670dc1c6043d03d4" - integrity sha512-KtDzw+/QZB28KAz5qVCsefTKbw5jeXAQBwdt/opgTFVcvTma1tC4Nq7sDCf3V8mCTRylmoRuKN6fIpbL43Gopg== +"@angular/localize@^11.0.2": + version "11.0.2" + resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-11.0.2.tgz#de0fce15d31c958478c280b3aecb398841146f9f" + integrity sha512-G7v/WPjno5QgY2XvYqK9pKP5lsaE17rP6/FIYhTFoA2TTSTJQ0mWlIQigcTvr+AT2t4U6nFJeteGuyIIvpMJYg== dependencies: "@babel/core" "7.8.3" glob "7.1.2" - yargs "15.3.0" + yargs "^16.1.1" "@angular/material@^9.2.3": version "9.2.4" @@ -2520,6 +2520,15 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + clone-deep@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" @@ -3510,6 +3519,11 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -3991,7 +4005,7 @@ get-caller-file@^1.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== -get-caller-file@^2.0.1: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -9284,6 +9298,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -9325,6 +9348,11 @@ y18n@^3.2.1: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" +y18n@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" + integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== + yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" @@ -9363,6 +9391,11 @@ yargs-parser@^18.1.0: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^20.2.2: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + yargs-parser@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" @@ -9421,6 +9454,19 @@ yargs@^13.2.4: y18n "^4.0.0" yargs-parser "^13.1.1" +yargs@^16.1.1: + version "16.1.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.1.1.tgz#5a4a095bd1ca806b0a50d0c03611d38034d219a1" + integrity sha512-hAD1RcFP/wfgfxgMVswPE+z3tlPFtxG8/yWUrG2i17sTWGCGqWnxKcLTF4cUKDUK8fzokwsmO9H0TDkRbMHy8w== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + yargs@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" From 923a848deb88a90b0081cb9aaaa2e979cd30dc1c Mon Sep 17 00:00:00 2001 From: Victor Usoltsev Date: Tue, 24 Nov 2020 19:43:16 +1300 Subject: [PATCH 26/82] Adds xml documentation generation. Better error handling when xml documentation is missing. --- src/Ombi/Extensions/StartupExtensions.cs | 85 ++++++++++++------------ src/Ombi/Ombi.csproj | 1 + 2 files changed, 42 insertions(+), 44 deletions(-) diff --git a/src/Ombi/Extensions/StartupExtensions.cs b/src/Ombi/Extensions/StartupExtensions.cs index d14dc2e9e..e42dfb609 100644 --- a/src/Ombi/Extensions/StartupExtensions.cs +++ b/src/Ombi/Extensions/StartupExtensions.cs @@ -1,13 +1,9 @@ using System; -using System.Collections.Generic; using System.IO; using System.Reflection; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.JsonPatch.Operations; -using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; @@ -15,18 +11,16 @@ using Microsoft.OpenApi.Models; using Ombi.Config; using Ombi.Helpers; using Ombi.Models.Identity; -using Swashbuckle.AspNetCore.Swagger; -using Swashbuckle.AspNetCore.SwaggerGen; namespace Ombi { - public static class StartupExtensions + public static class StartupExtensions { public static void AddSwagger(this IServiceCollection services) { services.AddSwaggerGen(c => { - c.SwaggerDoc("v1", new OpenApiInfo() + c.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", Title = "Ombi Api V1", @@ -37,35 +31,39 @@ namespace Ombi } }); - - - c.AddSecurityDefinition("ApiKey", new OpenApiSecurityScheme { Description = "API Key provided by Ombi. Example: \"ApiKey: {token}\"", Name = "ApiKey", - In = ParameterLocation.Header, + In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey }); + c.CustomSchemaIds(x => x.FullName); - var basePath = Path.GetDirectoryName(AppContext.BaseDirectory); - var xmlPath = Path.Combine(basePath, "Swagger.xml"); + try { - c.IncludeXmlComments(xmlPath); + string basePath = Path.GetDirectoryName(AppContext.BaseDirectory); + string assemblyName = Assembly.GetExecutingAssembly().GetName().Name; + string xmlPath = Path.Combine(basePath ?? string.Empty, $"{assemblyName}.xml"); + if (File.Exists(xmlPath)) + { + c.IncludeXmlComments(xmlPath); + } + else + { + Console.WriteLine($"Swagger failed to find documentation file at '{xmlPath}'."); + } } catch (Exception e) { Console.WriteLine(e); } - c.DescribeAllParametersInCamelCase(); }); } - - public static void AddAppSettingsValues(this IServiceCollection services, IConfigurationRoot configuration) { services.Configure(configuration.GetSection("ApplicationSettings")); @@ -78,13 +76,10 @@ namespace Ombi public static void AddJwtAuthentication(this IServiceCollection services, IConfigurationRoot configuration) { - var tokenOptions = configuration.GetSection("TokenAuthentication"); - var tokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(StartupSingleton.Instance.SecurityKey)), - RequireExpirationTime = true, ValidateLifetime = true, ValidAudience = "Ombi", @@ -92,33 +87,35 @@ namespace Ombi ClockSkew = TimeSpan.Zero, }; - services.AddAuthentication(options => - { - options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; - }).AddJwtBearer(x => - { - x.Audience = "Ombi"; - x.TokenValidationParameters = tokenValidationParameters; - x.Events = new JwtBearerEvents + services + .AddAuthentication(options => { - OnMessageReceived = context => + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddJwtBearer(x => + { + x.Audience = "Ombi"; + x.TokenValidationParameters = tokenValidationParameters; + x.Events = new JwtBearerEvents { - var accessToken = context.Request.Query["access_token"]; - - // If the request is for our hub... - var path = context.HttpContext.Request.Path; - if (!string.IsNullOrEmpty(accessToken) && - (path.StartsWithSegments("/hubs"))) + OnMessageReceived = context => { - // Read the token out of the query string - context.Token = accessToken; - } - return Task.CompletedTask; - } - }; - }); + var accessToken = context.Request.Query["access_token"]; + // If the request is for our hub... + var path = context.HttpContext.Request.Path; + if (!string.IsNullOrEmpty(accessToken) && + (path.StartsWithSegments("/hubs"))) + { + // Read the token out of the query string + context.Token = accessToken; + } + + return Task.CompletedTask; + } + }; + }); } } } \ No newline at end of file diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index 04a896596..9ac73264c 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -14,6 +14,7 @@ ClientApp\ $(DefaultItemExcludes);$(SpaRoot)node_modules\** true + bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml From fe711b6783d781f1474c0476b7d767f416d52f77 Mon Sep 17 00:00:00 2001 From: Victor Usoltsev Date: Tue, 24 Nov 2020 22:06:41 +1300 Subject: [PATCH 27/82] Ignores Trakt exceptions when retrieving optional tv show information. --- src/Ombi.Api.Trakt/TraktApi.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Ombi.Api.Trakt/TraktApi.cs b/src/Ombi.Api.Trakt/TraktApi.cs index c3faa5115..8bdabebe5 100644 --- a/src/Ombi.Api.Trakt/TraktApi.cs +++ b/src/Ombi.Api.Trakt/TraktApi.cs @@ -1,4 +1,5 @@  +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -48,7 +49,18 @@ namespace Ombi.Api.Trakt public async Task GetTvExtendedInfo(string imdbId) { - return await Client.Shows.GetShowAsync(imdbId, TraktExtendedOption.Full); + try + { + return await Client.Shows.GetShowAsync(imdbId, TraktExtendedOption.Full); + } + catch (Exception e) + { + // Ignore the exception since the information returned from this API is optional. + Console.WriteLine($"Failed to retrieve extended tv information from Trakt. IMDbId: '{imdbId}'."); + Console.WriteLine(e); + } + + return null; } } } From 9d716b7f7d69beff3cb17c1801fb0cbaae785e47 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 25 Nov 2020 21:09:09 +0000 Subject: [PATCH 28/82] Added a new Migrate option to run the migrations in and exit --- src/Ombi/Program.cs | 63 ++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/src/Ombi/Program.cs b/src/Ombi/Program.cs index 30ed06f01..4eae1c7f3 100644 --- a/src/Ombi/Program.cs +++ b/src/Ombi/Program.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using System.Linq; using System.Text; using Microsoft.AspNetCore.Hosting; @@ -7,14 +6,13 @@ using Ombi.Store.Context; using Ombi.Store.Entities; using CommandLine; using CommandLine.Text; -using Microsoft.AspNetCore; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.DependencyInjection; using Ombi.Extensions; using Ombi.Helpers; -using Ombi.Store.Context.MySql; -using Ombi.Store.Context.Sqlite; +using System.Threading.Tasks; +using System.Collections.Generic; namespace Ombi { @@ -22,7 +20,7 @@ namespace Ombi { private static string UrlArgs { get; set; } - public static void Main(string[] args) + public static async Task Main(string[] args) { Console.Title = "Ombi"; @@ -30,6 +28,7 @@ namespace Ombi var storagePath = string.Empty; var baseUrl = string.Empty; var demo = false; + var migrate = false; var result = Parser.Default.ParseArguments(args) .WithParsed(o => { @@ -37,6 +36,7 @@ namespace Ombi storagePath = o.StoragePath; baseUrl = o.BaseUrl; demo = o.Demo; + migrate = o.Migrate; }).WithNotParsed(err => { foreach (var e in err) @@ -62,11 +62,25 @@ namespace Ombi { var settingsDb = provider.GetRequiredService(); - var config = settingsDb.ApplicationConfigurations.ToList(); + if (migrate) + { + var migrationTasks = new List(); + var externalDb = provider.GetRequiredService(); + var ombiDb = provider.GetRequiredService(); + migrationTasks.Add(settingsDb.Database.MigrateAsync()); + migrationTasks.Add(ombiDb.Database.MigrateAsync()); + migrationTasks.Add(externalDb.Database.MigrateAsync()); + + Task.WaitAll(migrationTasks.ToArray()); + + Environment.Exit(0); + } + + var config = await settingsDb.ApplicationConfigurations.ToListAsync(); var url = config.FirstOrDefault(x => x.Type == ConfigurationTypes.Url); var dbBaseUrl = config.FirstOrDefault(x => x.Type == ConfigurationTypes.BaseUrl); var securityToken = config.FirstOrDefault(x => x.Type == ConfigurationTypes.SecurityToken); - CheckSecurityToken(securityToken, settingsDb, instance); + await CheckSecurityToken(securityToken, settingsDb, instance); if (url == null) { url = new ApplicationConfiguration @@ -74,11 +88,11 @@ namespace Ombi Type = ConfigurationTypes.Url, Value = "http://*:5000" }; - using (var tran = settingsDb.Database.BeginTransaction()) + using (var tran = await settingsDb.Database.BeginTransactionAsync()) { settingsDb.ApplicationConfigurations.Add(url); - settingsDb.SaveChanges(); - tran.Commit(); + await settingsDb.SaveChangesAsync(); + await tran.CommitAsync(); } urlValue = url.Value; @@ -88,10 +102,10 @@ namespace Ombi { url.Value = UrlArgs; - using (var tran = settingsDb.Database.BeginTransaction()) + using (var tran = await settingsDb.Database.BeginTransactionAsync()) { - settingsDb.SaveChanges(); - tran.Commit(); + await settingsDb.SaveChangesAsync(); + await tran.CommitAsync(); } urlValue = url.Value; @@ -111,11 +125,11 @@ namespace Ombi Value = baseUrl }; - using (var tran = settingsDb.Database.BeginTransaction()) + using (var tran = await settingsDb.Database.BeginTransactionAsync()) { settingsDb.ApplicationConfigurations.Add(dbBaseUrl); - settingsDb.SaveChanges(); - tran.Commit(); + await settingsDb.SaveChangesAsync(); + await tran.CommitAsync(); } } } @@ -123,10 +137,10 @@ namespace Ombi { dbBaseUrl.Value = baseUrl; - using (var tran = settingsDb.Database.BeginTransaction()) + using (var tran = await settingsDb.Database.BeginTransactionAsync()) { - settingsDb.SaveChanges(); - tran.Commit(); + await settingsDb.SaveChangesAsync(); + await tran.CommitAsync(); } } @@ -136,7 +150,7 @@ namespace Ombi } } - private static void CheckSecurityToken(ApplicationConfiguration securityToken, SettingsContext ctx, StartupSingleton instance) + private static async Task CheckSecurityToken(ApplicationConfiguration securityToken, SettingsContext ctx, StartupSingleton instance) { if (securityToken == null || string.IsNullOrEmpty(securityToken.Value)) { @@ -146,11 +160,11 @@ namespace Ombi Value = Guid.NewGuid().ToString("N") }; - using (var tran = ctx.Database.BeginTransaction()) + using (var tran = await ctx.Database.BeginTransactionAsync()) { ctx.ApplicationConfigurations.Add(securityToken); - ctx.SaveChanges(); - tran.Commit(); + await ctx.SaveChangesAsync(); + await tran.CommitAsync(); } } @@ -199,5 +213,8 @@ namespace Ombi [Option("demo", Required = false, HelpText = "Demo mode, you will never need to use this, fuck that fruit company...")] public bool Demo { get; set; } + [Option("migrate", Required = false, HelpText = "Will run the migrations then exit the application")] + public bool Migrate { get; set; } + } } From dfc55a9885aac6960ee06994cff12a61993759aa Mon Sep 17 00:00:00 2001 From: tidusjar Date: Thu, 26 Nov 2020 21:35:20 +0000 Subject: [PATCH 29/82] Potential fix for #3785 --- src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index d1fdd0788..e9ad9333c 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -154,6 +154,9 @@ namespace Ombi.Schedule.Jobs.Ombi plexContentMoviesToSend = plexContentMoviesToSend.Union(newPlexMovies).ToHashSet(); embyContentMoviesToSend = embyContentMoviesToSend.Union(newEmbyMovies).ToHashSet(); + plexContentMoviesToSend = plexContentMoviesToSend.DistinctBy(x => x.Id).ToHashSet(); + embyContentMoviesToSend = embyContentMoviesToSend.DistinctBy(x => x.Id).ToHashSet(); + var plexEpisodesToSend = FilterPlexEpisodes(_plex.GetAllEpisodes().Include(x => x.Series).AsNoTracking(), addedPlexEpisodesLogIds); var embyEpisodesToSend = FilterEmbyEpisodes(_emby.GetAllEpisodes().Include(x => x.Series).AsNoTracking(), From 1ddd19caf99baeceb7f26139ffcaa8bc100bbd1c Mon Sep 17 00:00:00 2001 From: Jaiver Pastor Date: Fri, 27 Nov 2020 12:50:16 +0100 Subject: [PATCH 30/82] Add informational message --- src/Ombi/Program.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Ombi/Program.cs b/src/Ombi/Program.cs index 4eae1c7f3..46b5c8559 100644 --- a/src/Ombi/Program.cs +++ b/src/Ombi/Program.cs @@ -64,6 +64,8 @@ namespace Ombi if (migrate) { + Console.WriteLine("Migrate in progress..."); + var migrationTasks = new List(); var externalDb = provider.GetRequiredService(); var ombiDb = provider.GetRequiredService(); @@ -73,6 +75,7 @@ namespace Ombi Task.WaitAll(migrationTasks.ToArray()); + Console.WriteLine("Migrate complete."); Environment.Exit(0); } From 8f6ef2cdb275a646535dff1fbbdc5a763258d1c3 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 9 Dec 2020 21:45:44 +0000 Subject: [PATCH 31/82] Fixed #3907 --- .../usermanagement.component.html | 2 +- .../usermanagement/usermanagement.component.ts | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html index 4b64e2f45..af789e168 100644 --- a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html +++ b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html @@ -7,7 +7,7 @@
-
+
diff --git a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.ts b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.ts index 4071c6b8f..b412091a8 100644 --- a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.ts +++ b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.ts @@ -10,7 +10,7 @@ import { SelectionModel } from "@angular/cdk/collections"; templateUrl: "./usermanagement.component.html", styleUrls: ["./usermanagement.component.scss"], }) -export class UserManagementComponent implements OnInit, AfterViewInit { +export class UserManagementComponent implements OnInit { public displayedColumns: string[] = ['select', 'username', 'alias', 'email', 'roles', 'remainingRequests', 'nextRequestDue', 'lastLoggedIn', 'userType', 'actions', 'welcome']; @@ -32,13 +32,15 @@ export class UserManagementComponent implements OnInit, AfterViewInit { constructor(private identityService: IdentityService, private settingsService: SettingsService, private notificationService: NotificationService, - private plexSettings: SettingsService) { } + private plexSettings: SettingsService) { + this.dataSource = new MatTableDataSource(); + } public async ngOnInit() { this.users = await this.identityService.getUsers().toPromise(); - this.dataSource = new MatTableDataSource(this.users); + this.dataSource.sort = this.sort; this.plexSettings.getPlex().subscribe(x => this.plexEnabled = x.enable); @@ -47,10 +49,6 @@ export class UserManagementComponent implements OnInit, AfterViewInit { this.settingsService.getEmailNotificationSettings().subscribe(x => this.emailSettings = x); } - public ngAfterViewInit(): void { - this.dataSource.sort = this.sort; - } - public welcomeEmail(user: IUser) { if (!user.emailAddress) { this.notificationService.error("The user needs an email address."); @@ -103,6 +101,9 @@ export class UserManagementComponent implements OnInit, AfterViewInit { } public isAllSelected() { + if (!this.dataSource) { + return; + } const numSelected = this.selection.selected.length; const numRows = this.dataSource.data.length; return numSelected === numRows; @@ -110,6 +111,9 @@ export class UserManagementComponent implements OnInit, AfterViewInit { public masterToggle() { + if (!this.dataSource) { + return; + } this.isAllSelected() ? this.selection.clear() : this.dataSource.data.forEach(row => this.selection.select(row)); From e22cc1ca150d4457d5097915086be0e50bb93683 Mon Sep 17 00:00:00 2001 From: David Roizenman Date: Wed, 9 Dec 2020 17:51:37 -0800 Subject: [PATCH 32/82] Remove remaining smart quotes --- src/Ombi/Views/Shared/_Layout.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi/Views/Shared/_Layout.cshtml b/src/Ombi/Views/Shared/_Layout.cshtml index 22c6c98f1..5cee9c275 100644 --- a/src/Ombi/Views/Shared/_Layout.cshtml +++ b/src/Ombi/Views/Shared/_Layout.cshtml @@ -82,7 +82,7 @@ - +
- +
@@ -125,4 +125,4 @@ - \ No newline at end of file + diff --git a/src/Ombi/ClientApp/src/app/search/moviesearch.component.html b/src/Ombi/ClientApp/src/app/search/moviesearch.component.html index 30507d004..dd95d8a74 100644 --- a/src/Ombi/ClientApp/src/app/search/moviesearch.component.html +++ b/src/Ombi/ClientApp/src/app/search/moviesearch.component.html @@ -168,6 +168,9 @@ {{'Search.ViewOnEmby' | translate}} + {{'Search.ViewOnJellyfin' | + translate}} + +
+
+
+
+ + Server Name + + +
+ +
+ + Hostname / IP + + + +
+ + Server ID + + +
+ + Port + + + + + SSL + +
+
+ + API Key + + +
+
+ + Base URL + + +
+
+ + Externally Facing Hostname + + + + Current URL: "{{server.serverHostname}}/#!/{{settings.isJellyfin ? ("itemdetails"): ("item/item")}}.html?id=1" + Current URL: "https://app.jellyfin.media/#!/{{settings.isJellyfin ? ("itemdetails"): ("item/item")}}.html?id=1 + +
+ + +
+
+ +
+
+
+
+ +
+
+
+ + + + + +
+
+
+ +
+
+
+
+
+
+ +
+
+
+ + + diff --git a/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.scss b/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.scss new file mode 100644 index 000000000..649201f48 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.scss @@ -0,0 +1,41 @@ +@import "~styles/shared.scss"; +.small-middle-container { + margin: auto; + width: 95%; + margin-top: 10px; +} + +.col-md-10 { + display: grid; +} + +.col-md-2 { + display: contents; +} + +.control-label { + font-weight: 400; +} + +.row { + display: block; +} + +.btn-danger-outline { + background-color: #E84C3D; +} + +.btn-success-outline { + background-color: #1b9d1b; +} + +::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); + color: inherit; +} + +@media (min-width:1440px) { + .col-md-2 { + display: inline-table; + } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.ts b/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.ts new file mode 100644 index 000000000..2ae0d331d --- /dev/null +++ b/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.ts @@ -0,0 +1,95 @@ +import { Component, OnInit } from "@angular/core"; + +import { IJellyfinServer, IJellyfinSettings } from "../../interfaces"; +import { JellyfinService, JobService, NotificationService, SettingsService, TesterService } from "../../services"; +import { MatTabChangeEvent } from "@angular/material/tabs"; +import {FormControl} from '@angular/forms'; + +@Component({ + templateUrl: "./jellyfin.component.html", + styleUrls: ["./jellyfin.component.scss"] +}) +export class JellyfinComponent implements OnInit { + + public settings: IJellyfinSettings; + public hasDiscoveredOrDirty: boolean; + selected = new FormControl(0); + + constructor(private settingsService: SettingsService, + private notificationService: NotificationService, + private testerService: TesterService, + private jobService: JobService, + private jellyfinService: JellyfinService) { } + + public ngOnInit() { + this.settingsService.getJellyfin().subscribe(x => this.settings = x); + } + + public async discoverServerInfo(server: IJellyfinServer) { + const result = await this.jellyfinService.getPublicInfo(server).toPromise(); + server.name = result.serverName; + server.serverId = result.id; + this.hasDiscoveredOrDirty = true; + } + + public addTab(event: MatTabChangeEvent) { + const tabName = event.tab.textLabel; + if (tabName == "Add Server"){ + if (this.settings.servers == null) { + this.settings.servers = []; + } + this.settings.servers.push({ + name: "New " + this.settings.servers.length + "*", + id: Math.floor(Math.random() * (99999 - 0 + 1) + 1), + apiKey: "", + administratorId: "", + enableEpisodeSearching: false, + ip: "", + port: 0, + ssl: false, + subDir: "", + } as IJellyfinServer); + this.selected.setValue(this.settings.servers.length - 1); + } + } + + public toggle() { + this.hasDiscoveredOrDirty = true; + } + + public test(server: IJellyfinServer) { + this.testerService.jellyfinTest(server).subscribe(x => { + if (x === true) { + this.notificationService.success(`Successfully connected to the Jellyfin server ${server.name}!`); + } else { + this.notificationService.error(`We could not connect to the Jellyfin server ${server.name}!`); + } + }); + } + + public removeServer(server: IJellyfinServer) { + const index = this.settings.servers.indexOf(server, 0); + if (index > -1) { + this.settings.servers.splice(index, 1); + this.selected.setValue(this.settings.servers.length - 1); + } + } + + public save() { + this.settingsService.saveJellyfin(this.settings).subscribe(x => { + if (x) { + this.notificationService.success("Successfully saved Jellyfin settings"); + } else { + this.notificationService.success("There was an error when saving the Jellyfin settings"); + } + }); + } + + public runCacher(): void { + this.jobService.runJellyfinCacher().subscribe(x => { + if(x) { + this.notificationService.success("Triggered the Jellyfin Content Cacher"); + } + }); + } +} diff --git a/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.html b/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.html index ec2a42c99..982643e2e 100644 --- a/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.html +++ b/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.html @@ -78,6 +78,13 @@ +
+ + + The Jellyfin Sync is required + +
+
diff --git a/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.ts b/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.ts index 914d126d0..83508e752 100644 --- a/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.ts +++ b/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.ts @@ -24,6 +24,7 @@ export class JobsComponent implements OnInit { automaticUpdater: [x.automaticUpdater, Validators.required], couchPotatoSync: [x.couchPotatoSync, Validators.required], embyContentSync: [x.embyContentSync, Validators.required], + jellyfinContentSync: [x.jellyfinContentSync, Validators.required], plexContentSync: [x.plexContentSync, Validators.required], userImporter: [x.userImporter, Validators.required], sonarrSync: [x.sonarrSync, Validators.required], diff --git a/src/Ombi/ClientApp/src/app/settings/settings.module.ts b/src/Ombi/ClientApp/src/app/settings/settings.module.ts index 58ceb9c64..b7f4536b5 100644 --- a/src/Ombi/ClientApp/src/app/settings/settings.module.ts +++ b/src/Ombi/ClientApp/src/app/settings/settings.module.ts @@ -8,7 +8,7 @@ import { ClipboardModule } from "ngx-clipboard"; import { AuthGuard } from "../auth/auth.guard"; import { AuthService } from "../auth/auth.service"; import { - CouchPotatoService, EmbyService, IssuesService, JobService, LidarrService, MobileService, NotificationMessageService, PlexService, RadarrService, + CouchPotatoService, EmbyService, JellyfinService, IssuesService, JobService, LidarrService, MobileService, NotificationMessageService, PlexService, RadarrService, RequestRetryService, SonarrService, TesterService, ValidationService, SystemService, FileDownloadService, TheMovieDbService } from "../services"; @@ -19,6 +19,7 @@ import { CouchPotatoComponent } from "./couchpotato/couchpotato.component"; import { CustomizationComponent } from "./customization/customization.component"; import { DogNzbComponent } from "./dognzb/dognzb.component"; import { EmbyComponent } from "./emby/emby.component"; +import { JellyfinComponent } from "./jellyfin/jellyfin.component"; import { FailedRequestsComponent } from "./failedrequests/failedrequests.component"; import { IssuesComponent } from "./issues/issues.component"; import { JobsComponent } from "./jobs/jobs.component"; @@ -73,6 +74,7 @@ const routes: Routes = [ { path: "About", component: AboutComponent, canActivate: [AuthGuard] }, { path: "Plex", component: PlexComponent, canActivate: [AuthGuard] }, { path: "Emby", component: EmbyComponent, canActivate: [AuthGuard] }, + { path: "Jellyfin", component: JellyfinComponent, canActivate: [AuthGuard] }, { path: "Sonarr", component: SonarrComponent, canActivate: [AuthGuard] }, { path: "Radarr", component: RadarrComponent, canActivate: [AuthGuard] }, { path: "LandingPage", component: LandingPageComponent, canActivate: [AuthGuard] }, @@ -131,6 +133,7 @@ const routes: Routes = [ OmbiComponent, PlexComponent, EmbyComponent, + JellyfinComponent, JobsComponent, LandingPageComponent, CustomizationComponent, @@ -182,6 +185,7 @@ const routes: Routes = [ IssuesService, PlexService, EmbyService, + JellyfinService, MobileService, NotificationMessageService, LidarrService, diff --git a/src/Ombi/ClientApp/src/app/settings/settingsmenu.component.html b/src/Ombi/ClientApp/src/app/settings/settingsmenu.component.html index 20b843139..ce97ee200 100644 --- a/src/Ombi/ClientApp/src/app/settings/settingsmenu.component.html +++ b/src/Ombi/ClientApp/src/app/settings/settingsmenu.component.html @@ -14,7 +14,8 @@ - + + @@ -61,4 +62,4 @@ - \ No newline at end of file + diff --git a/src/Ombi/ClientApp/src/app/settings/usermanagement/usermanagement.component.ts b/src/Ombi/ClientApp/src/app/settings/usermanagement/usermanagement.component.ts index 00483e4f2..77447ce21 100644 --- a/src/Ombi/ClientApp/src/app/settings/usermanagement/usermanagement.component.ts +++ b/src/Ombi/ClientApp/src/app/settings/usermanagement/usermanagement.component.ts @@ -2,7 +2,7 @@ import { ICheckbox, IUserManagementSettings } from "../../interfaces"; import { IUsersModel } from "../../interfaces"; -import { EmbyService, IdentityService, JobService, NotificationService, PlexService, SettingsService } from "../../services"; +import { EmbyService, JellyfinService, IdentityService, JobService, NotificationService, PlexService, SettingsService } from "../../services"; @Component({ templateUrl: "./usermanagement.component.html", @@ -12,6 +12,7 @@ export class UserManagementComponent implements OnInit { public plexEnabled: boolean; public embyEnabled: boolean; + public jellyfinEnabled: boolean; public settings: IUserManagementSettings; public claims: ICheckbox[]; @@ -23,6 +24,10 @@ export class UserManagementComponent implements OnInit { public filteredEmbyUsers: IUsersModel[]; public bannedEmbyUsers: IUsersModel[] = []; + public jellyfinUsers: IUsersModel[]; + public filteredJellyfinUsers: IUsersModel[]; + public bannedJellyfinUsers: IUsersModel[] = []; + public enableImportButton = false; constructor(private readonly settingsService: SettingsService, @@ -30,14 +35,15 @@ export class UserManagementComponent implements OnInit { private readonly identityService: IdentityService, private readonly plexService: PlexService, private readonly jobService: JobService, - private readonly embyService: EmbyService) { + private readonly embyService: EmbyService, + private readonly jellyfinService: JellyfinService) { } public ngOnInit(): void { this.settingsService.getUserManagementSettings().subscribe(x => { this.settings = x; - if(x.importEmbyUsers || x.importPlexUsers) { + if(x.importEmbyUsers || x.importJellyfinUsers || x.importPlexUsers) { this.enableImportButton = true; } @@ -65,6 +71,18 @@ export class UserManagementComponent implements OnInit { }); }); + this.jellyfinService.getUsers().subscribe(f => { + this.jellyfinUsers = f; + this.jellyfinUsers.forEach((jellyfin) => { + const isExcluded = this.settings.bannedPlexUserIds.some((val) => { + return jellyfin.id === val; + }); + if (isExcluded) { + this.bannedJellyfinUsers.push(jellyfin); + } + }); + }); + this.identityService.getAllAvailableClaims().subscribe(c => { this.claims = c; @@ -80,6 +98,7 @@ export class UserManagementComponent implements OnInit { }); this.settingsService.getPlex().subscribe(x => this.plexEnabled = x.enable); this.settingsService.getEmby().subscribe(x => this.embyEnabled = x.enable); + this.settingsService.getJellyfin().subscribe(x => this.jellyfinEnabled = x.enable); } public submit(): void { @@ -89,8 +108,9 @@ export class UserManagementComponent implements OnInit { this.settings.defaultRoles = enabledClaims.map((claim) => claim.value); this.settings.bannedPlexUserIds = this.bannedPlexUsers.map((u) => u.id); this.settings.bannedEmbyUserIds = this.bannedEmbyUsers.map((u) => u.id); + this.settings.bannedJellyfinUserIds = this.bannedJellyfinUsers.map((u) => u.id); - if(this.settings.importEmbyUsers || this.settings.importPlexUsers) { + if(this.settings.importEmbyUsers || this.settings.importJellyfinUsers || this.settings.importPlexUsers) { this.enableImportButton = true; } @@ -111,10 +131,15 @@ export class UserManagementComponent implements OnInit { this.filteredEmbyUsers = this.filter(event.query, this.embyUsers); } + public filterJellyfinList(event: any) { + this.filteredJellyfinUsers = this.filter(event.query, this.jellyfinUsers); + } + public runImporter(): void { this.jobService.runPlexImporter().subscribe(); this.jobService.runEmbyImporter().subscribe(); + this.jobService.runJellyfinImporter().subscribe(); } private filter(query: string, users: IUsersModel[]): IUsersModel[] { diff --git a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html index 4b64e2f45..9490452ae 100644 --- a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html +++ b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html @@ -87,7 +87,9 @@
+ Emby User + Jellyfin User + diff --git a/src/Ombi/ClientApp/src/app/wizard/jellyfin/jellyfin.component.html b/src/Ombi/ClientApp/src/app/wizard/jellyfin/jellyfin.component.html new file mode 100644 index 000000000..38c220b04 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/wizard/jellyfin/jellyfin.component.html @@ -0,0 +1,31 @@ + +
+
+
+
+ + + +
+ +
+ + + + +
+
+ Enable SSL +
+
+ + + + +
+
+ Save
+
+
+
+
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/wizard/jellyfin/jellyfin.component.ts b/src/Ombi/ClientApp/src/app/wizard/jellyfin/jellyfin.component.ts new file mode 100644 index 000000000..4c42c88ac --- /dev/null +++ b/src/Ombi/ClientApp/src/app/wizard/jellyfin/jellyfin.component.ts @@ -0,0 +1,52 @@ +import { Component, OnInit } from "@angular/core"; + +import { JellyfinService } from "../../services"; +import { NotificationService } from "../../services"; + +import { IJellyfinSettings } from "../../interfaces"; + +@Component({ + selector: "wizard-jellyfin", + templateUrl: "./jellyfin.component.html", +}) +export class JellyfinComponent implements OnInit { + + public jellyfinSettings: IJellyfinSettings; + + constructor(private jellyfinService: JellyfinService, + private notificationService: NotificationService) { + } + + public ngOnInit() { + this.jellyfinSettings = { + servers: [], + isJellyfin: false, + id: 0, + enable: true, + }; + this.jellyfinSettings.servers.push({ + ip: "", + administratorId: "", + id: 0, + apiKey: "", + enableEpisodeSearching: false, + name: "Default", + port: 8096, + ssl: false, + subDir: "", + serverHostname: "", + serverId: undefined + }); + } + + public save() { + this.jellyfinService.logIn(this.jellyfinSettings).subscribe(x => { + if (x == null || !x.servers[0].apiKey) { + this.notificationService.error("Username or password was incorrect. Could not authenticate with Jellyfin."); + return; + } + + this.notificationService.success("Done! Please press next"); + }); + } +} diff --git a/src/Ombi/ClientApp/src/app/wizard/mediaserver/mediaserver.component.html b/src/Ombi/ClientApp/src/app/wizard/mediaserver/mediaserver.component.html index fd561a227..afa626f0d 100644 --- a/src/Ombi/ClientApp/src/app/wizard/mediaserver/mediaserver.component.html +++ b/src/Ombi/ClientApp/src/app/wizard/mediaserver/mediaserver.component.html @@ -11,6 +11,13 @@ +
+
+ + + +
+
- \ No newline at end of file + diff --git a/src/Ombi/ClientApp/src/app/wizard/welcome/welcome.component.html b/src/Ombi/ClientApp/src/app/wizard/welcome/welcome.component.html index 416af7b09..dfec7f8d9 100644 --- a/src/Ombi/ClientApp/src/app/wizard/welcome/welcome.component.html +++ b/src/Ombi/ClientApp/src/app/wizard/welcome/welcome.component.html @@ -33,6 +33,17 @@ + +
+ Jellyfin + +
+ + +
+ +
+
Create a local admin diff --git a/src/Ombi/ClientApp/src/app/wizard/wizard.module.ts b/src/Ombi/ClientApp/src/app/wizard/wizard.module.ts index 5438e8e9c..1e34a1de9 100644 --- a/src/Ombi/ClientApp/src/app/wizard/wizard.module.ts +++ b/src/Ombi/ClientApp/src/app/wizard/wizard.module.ts @@ -7,11 +7,13 @@ import { MatStepperModule } from "@angular/material/stepper"; import { CreateAdminComponent } from "./createadmin/createadmin.component"; import { EmbyComponent } from "./emby/emby.component"; +import { JellyfinComponent } from "./jellyfin/jellyfin.component"; import { MediaServerComponent } from "./mediaserver/mediaserver.component"; import { PlexComponent } from "./plex/plex.component"; import { WelcomeComponent } from "./welcome/welcome.component"; import { EmbyService } from "../services"; +import { JellyfinService } from "../services"; import { PlexService } from "../services"; import { IdentityService } from "../services"; import { PlexOAuthService } from "../services"; @@ -23,6 +25,7 @@ const routes: Routes = [ { path: "MediaServer", component: MediaServerComponent}, { path: "Plex", component: PlexComponent}, { path: "Emby", component: EmbyComponent}, + { path: "Jellyfin", component: JellyfinComponent}, { path: "CreateAdmin", component: CreateAdminComponent}, ]; @NgModule({ @@ -40,6 +43,7 @@ const routes: Routes = [ PlexComponent, CreateAdminComponent, EmbyComponent, + JellyfinComponent, ], exports: [ RouterModule, @@ -48,6 +52,7 @@ const routes: Routes = [ PlexService, IdentityService, EmbyService, + JellyfinService, PlexOAuthService, ], diff --git a/src/Ombi/Controllers/V1/External/JellyfinController.cs b/src/Ombi/Controllers/V1/External/JellyfinController.cs new file mode 100644 index 000000000..27663a5b6 --- /dev/null +++ b/src/Ombi/Controllers/V1/External/JellyfinController.cs @@ -0,0 +1,96 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Ombi.Api.Jellyfin; +using Ombi.Api.Jellyfin.Models; +using Ombi.Api.Plex; +using Ombi.Attributes; +using Ombi.Core.Settings; +using Ombi.Core.Settings.Models.External; +using Ombi.Helpers; +using Ombi.Models.External; + +namespace Ombi.Controllers.V1.External +{ + [Admin] + [ApiV1] + [Produces("application/json")] + public class JellyfinController : Controller + { + + public JellyfinController(IJellyfinApiFactory jellyfin, ISettingsService jellyfinSettings) + { + JellyfinApi = jellyfin; + JellyfinSettings = jellyfinSettings; + } + + private IJellyfinApiFactory JellyfinApi { get; } + private ISettingsService JellyfinSettings { get; } + + /// + /// Signs into the Jellyfin Api + /// + /// The request. + /// + [HttpPost] + [AllowAnonymous] + public async Task SignIn([FromBody] JellyfinSettings request) + { + // Check if settings exist since we allow anon... + var settings = await JellyfinSettings.GetSettingsAsync(); + if (settings?.Servers?.Any() ?? false) return null; + + var client = await JellyfinApi.CreateClient(); + request.Enable = true; + var firstServer = request.Servers.FirstOrDefault(); + // Test that we can connect + var result = await client.GetUsers(firstServer.FullUri, firstServer.ApiKey); + + if (result != null && result.Any()) + { + firstServer.AdministratorId = result.FirstOrDefault(x => x.Policy.IsAdministrator)?.Id ?? string.Empty; + await JellyfinSettings.SaveSettingsAsync(request); + + return request; + } + return null; + } + + [HttpPost("info")] + public async Task GetServerInfo([FromBody] JellyfinServers server) + { + var client = await JellyfinApi.CreateClient(); + var result = await client.GetPublicInformation(server.FullUri); + return result; + } + + /// + /// Gets the jellyfin users. + /// + /// + [HttpGet("users")] + public async Task> JellyfinUsers() + { + var vm = new List(); + var s = await JellyfinSettings.GetSettingsAsync(); + var client = JellyfinApi.CreateClient(s); + foreach (var server in s?.Servers ?? new List()) + { + var users = await client.GetUsers(server.FullUri, server.ApiKey); + if (users != null && users.Any()) + { + vm.AddRange(users.Select(u => new UsersViewModel + { + Username = u.Name, + Id = u.Id + })); + } + } + + // Filter out any dupes + return vm.DistinctBy(x => x.Id); + } + } +} diff --git a/src/Ombi/Controllers/V1/JobController.cs b/src/Ombi/Controllers/V1/JobController.cs index 6ffbf8486..7f05474dc 100644 --- a/src/Ombi/Controllers/V1/JobController.cs +++ b/src/Ombi/Controllers/V1/JobController.cs @@ -6,6 +6,7 @@ using Ombi.Attributes; using Ombi.Helpers; using Ombi.Schedule.Jobs; using Ombi.Schedule.Jobs.Emby; +using Ombi.Schedule.Jobs.Jellyfin; using Ombi.Schedule.Jobs.Ombi; using Ombi.Schedule.Jobs.Plex; using Ombi.Schedule.Jobs.Radarr; @@ -92,13 +93,13 @@ namespace Ombi.Controllers.V1 } /// - /// Runs the Emby User importer + /// Runs the Jellyfin User importer /// /// [HttpPost("embyuserimporter")] - public async Task EmbyUserImporter() + public async Task JellyfinUserImporter() { - await OmbiQuartz.TriggerJob(nameof(IEmbyUserImporter), "Emby"); + await OmbiQuartz.TriggerJob(nameof(IJellyfinUserImporter), "Jellyfin"); return true; } @@ -135,6 +136,17 @@ namespace Ombi.Controllers.V1 return true; } + /// + /// Runs the Jellyfin Content Cacher + /// + /// + [HttpPost("jellyfincontentcacher")] + public async Task StartJellyfinContentCacher() + { + await OmbiQuartz.TriggerJob(nameof(IJellyfinContentSync), "Jellyfin"); + return true; + } + /// /// Runs the Arr Availability Checker /// @@ -165,4 +177,4 @@ namespace Ombi.Controllers.V1 return true; } } -} \ No newline at end of file +} diff --git a/src/Ombi/Controllers/V1/SettingsController.cs b/src/Ombi/Controllers/V1/SettingsController.cs index 1d340b4cd..9e7343472 100644 --- a/src/Ombi/Controllers/V1/SettingsController.cs +++ b/src/Ombi/Controllers/V1/SettingsController.cs @@ -10,6 +10,7 @@ using AutoMapper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Ombi.Api.Emby; +using Ombi.Api.Jellyfin; using Ombi.Api.Github; using Ombi.Attributes; using Ombi.Core.Engine; @@ -29,6 +30,7 @@ using Ombi.Extensions; using Quartz; using Ombi.Schedule.Jobs; using Ombi.Schedule.Jobs.Emby; +using Ombi.Schedule.Jobs.Jellyfin; using Ombi.Schedule.Jobs.Sonarr; using Ombi.Schedule.Jobs.Lidarr; @@ -48,6 +50,7 @@ namespace Ombi.Controllers.V1 IMapper mapper, INotificationTemplatesRepository templateRepo, IEmbyApiFactory embyApi, + IJellyfinApiFactory jellyfinApi, ICacheService memCache, IGithubApi githubApi, IRecentlyAddedEngine engine) @@ -56,6 +59,7 @@ namespace Ombi.Controllers.V1 Mapper = mapper; TemplateRepository = templateRepo; _embyApi = embyApi; + _jellyfinApi = jellyfinApi; _cache = memCache; _githubApi = githubApi; _recentlyAdded = engine; @@ -65,6 +69,7 @@ namespace Ombi.Controllers.V1 private IMapper Mapper { get; } private INotificationTemplatesRepository TemplateRepository { get; } private readonly IEmbyApiFactory _embyApi; + private readonly IJellyfinApiFactory _jellyfinApi; private readonly ICacheService _cache; private readonly IGithubApi _githubApi; private readonly IRecentlyAddedEngine _recentlyAdded; @@ -234,6 +239,42 @@ namespace Ombi.Controllers.V1 return result; } + /// + /// Gets the Jellyfin Settings. + /// + /// + [HttpGet("jellyfin")] + public async Task JellyfinSettings() + { + return await Get(); + } + + /// + /// Save the Jellyfin settings. + /// + /// The jellyfin. + /// + [HttpPost("jellyfin")] + public async Task JellyfinSettings([FromBody]JellyfinSettings jellyfin) + { + if (jellyfin.Enable) + { + var client = await _jellyfinApi.CreateClient(); + foreach (var server in jellyfin.Servers) + { + var users = await client.GetUsers(server.FullUri, server.ApiKey); + var admin = users.FirstOrDefault(x => x.Policy.IsAdministrator); + server.AdministratorId = admin?.Id; + } + } + var result = await Save(jellyfin); + if (result) + { + await OmbiQuartz.TriggerJob(nameof(IJellyfinContentSync), "Jellyfin"); + } + return result; + } + /// /// Gets the Landing Page Settings. /// @@ -566,6 +607,7 @@ namespace Ombi.Controllers.V1 j.AutomaticUpdater = j.AutomaticUpdater.HasValue() ? j.AutomaticUpdater : JobSettingsHelper.Updater(j); j.CouchPotatoSync = j.CouchPotatoSync.HasValue() ? j.CouchPotatoSync : JobSettingsHelper.CouchPotato(j); j.EmbyContentSync = j.EmbyContentSync.HasValue() ? j.EmbyContentSync : JobSettingsHelper.EmbyContent(j); + j.JellyfinContentSync = j.JellyfinContentSync.HasValue() ? j.JellyfinContentSync : JobSettingsHelper.JellyfinContent(j); j.PlexContentSync = j.PlexContentSync.HasValue() ? j.PlexContentSync : JobSettingsHelper.PlexContent(j); j.UserImporter = j.UserImporter.HasValue() ? j.UserImporter : JobSettingsHelper.UserImporter(j); j.SickRageSync = j.SickRageSync.HasValue() ? j.SickRageSync : JobSettingsHelper.SickRageSync(j); diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index 9ac73264c..2c615f57c 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -71,6 +71,7 @@ + From 2f1471c61a3c215bd22783f8015dd0e7f298c0b1 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Thu, 10 Dec 2020 10:32:56 -0500 Subject: [PATCH 34/82] Update Jellyfin authorization header We still use X-Emby-Authorization instead of X-Jellyfin-Authorization. Co-authored-by: Cody Robibero --- src/Ombi.Api.Jellyfin/JellyfinApi.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi.Api.Jellyfin/JellyfinApi.cs b/src/Ombi.Api.Jellyfin/JellyfinApi.cs index 1265791d0..a8d94eca6 100644 --- a/src/Ombi.Api.Jellyfin/JellyfinApi.cs +++ b/src/Ombi.Api.Jellyfin/JellyfinApi.cs @@ -67,7 +67,7 @@ namespace Ombi.Api.Jellyfin request.AddJsonBody(body); - request.AddHeader("X-Jellyfin-Authorization", + request.AddHeader("X-Emby-Authorization", $"MediaBrowser Client=\"Ombi\", Device=\"Ombi\", DeviceId=\"v3\", Version=\"v3\""); AddHeaders(request, apiKey); From b48c8f7592c3c50767ecd06af73a4b2c50bb7e5e Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Fri, 11 Dec 2020 00:00:38 -0500 Subject: [PATCH 35/82] Add database migration to SQLite --- .../20201211042424_Jellyfin.Designer.cs | 1221 +++++++++++++++++ .../OmbiSqlite/20201211042424_Jellyfin.cs | 69 + 2 files changed, 1290 insertions(+) create mode 100644 src/Ombi.Store/Migrations/OmbiSqlite/20201211042424_Jellyfin.Designer.cs create mode 100644 src/Ombi.Store/Migrations/OmbiSqlite/20201211042424_Jellyfin.cs diff --git a/src/Ombi.Store/Migrations/OmbiSqlite/20201211042424_Jellyfin.Designer.cs b/src/Ombi.Store/Migrations/OmbiSqlite/20201211042424_Jellyfin.Designer.cs new file mode 100644 index 000000000..4fc5f9456 --- /dev/null +++ b/src/Ombi.Store/Migrations/OmbiSqlite/20201211042424_Jellyfin.Designer.cs @@ -0,0 +1,1221 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context.Sqlite; + +namespace Ombi.Store.Migrations.OmbiSqlite +{ + [DbContext(typeof(OmbiSqliteContext))] + [Migration("20201211042424_Jellyfin")] + partial class Jellyfin + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.1"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AuditArea") + .HasColumnType("INTEGER"); + + b.Property("AuditType") + .HasColumnType("INTEGER"); + + b.Property("DateTime") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("User") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.MobileDevices", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("Token") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("MobileDevices"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Agent") + .HasColumnType("INTEGER"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("Message") + .HasColumnType("TEXT"); + + b.Property("NotificationType") + .HasColumnType("INTEGER"); + + b.Property("Subject") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("PlayerId") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("Alias") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("EpisodeRequestLimit") + .HasColumnType("INTEGER"); + + b.Property("Language") + .HasColumnType("TEXT"); + + b.Property("LastLoggedIn") + .HasColumnType("TEXT"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("MovieRequestLimit") + .HasColumnType("INTEGER"); + + b.Property("MusicRequestLimit") + .HasColumnType("INTEGER"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("ProviderUserId") + .HasColumnType("TEXT"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserAccessToken") + .HasColumnType("TEXT"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("UserType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("AlbumId") + .HasColumnType("TEXT"); + + b.Property("ContentId") + .HasColumnType("INTEGER"); + + b.Property("ContentType") + .HasColumnType("INTEGER"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestQueue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Completed") + .HasColumnType("TEXT"); + + b.Property("Dts") + .HasColumnType("TEXT"); + + b.Property("Error") + .HasColumnType("TEXT"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("RequestQueue"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("ArtistName") + .HasColumnType("TEXT"); + + b.Property("Available") + .HasColumnType("INTEGER"); + + b.Property("Cover") + .HasColumnType("TEXT"); + + b.Property("Denied") + .HasColumnType("INTEGER"); + + b.Property("DeniedReason") + .HasColumnType("TEXT"); + + b.Property("Disk") + .HasColumnType("TEXT"); + + b.Property("ForeignAlbumId") + .HasColumnType("TEXT"); + + b.Property("ForeignArtistId") + .HasColumnType("TEXT"); + + b.Property("MarkedAsApproved") + .HasColumnType("TEXT"); + + b.Property("MarkedAsAvailable") + .HasColumnType("TEXT"); + + b.Property("MarkedAsDenied") + .HasColumnType("TEXT"); + + b.Property("Rating") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("RequestedByAlias") + .HasColumnType("TEXT"); + + b.Property("RequestedDate") + .HasColumnType("TEXT"); + + b.Property("RequestedUserId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("Available") + .HasColumnType("INTEGER"); + + b.Property("Denied") + .HasColumnType("INTEGER"); + + b.Property("DeniedReason") + .HasColumnType("TEXT"); + + b.Property("IssueId") + .HasColumnType("INTEGER"); + + b.Property("MarkedAsApproved") + .HasColumnType("TEXT"); + + b.Property("MarkedAsAvailable") + .HasColumnType("TEXT"); + + b.Property("MarkedAsDenied") + .HasColumnType("TEXT"); + + b.Property("ParentRequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("RequestedByAlias") + .HasColumnType("TEXT"); + + b.Property("RequestedDate") + .HasColumnType("TEXT"); + + b.Property("RequestedUserId") + .HasColumnType("TEXT"); + + b.Property("SeriesType") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Comment") + .HasColumnType("TEXT"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("IssuesId") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDate") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("IssueCategoryId") + .HasColumnType("INTEGER"); + + b.Property("IssueId") + .HasColumnType("INTEGER"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("ResovledDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Subject") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("UserReportedId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("Available") + .HasColumnType("INTEGER"); + + b.Property("Background") + .HasColumnType("TEXT"); + + b.Property("Denied") + .HasColumnType("INTEGER"); + + b.Property("DeniedReason") + .HasColumnType("TEXT"); + + b.Property("DigitalReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("IssueId") + .HasColumnType("INTEGER"); + + b.Property("LangCode") + .HasColumnType("TEXT"); + + b.Property("MarkedAsApproved") + .HasColumnType("TEXT"); + + b.Property("MarkedAsAvailable") + .HasColumnType("TEXT"); + + b.Property("MarkedAsDenied") + .HasColumnType("TEXT"); + + b.Property("Overview") + .HasColumnType("TEXT"); + + b.Property("PosterPath") + .HasColumnType("TEXT"); + + b.Property("QualityOverride") + .HasColumnType("INTEGER"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("RequestedByAlias") + .HasColumnType("TEXT"); + + b.Property("RequestedDate") + .HasColumnType("TEXT"); + + b.Property("RequestedUserId") + .HasColumnType("TEXT"); + + b.Property("RootPathOverride") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("TEXT"); + + b.Property("TheMovieDbId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EpisodeCount") + .HasColumnType("INTEGER"); + + b.Property("RequestDate") + .HasColumnType("TEXT"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Background") + .HasColumnType("TEXT"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("Overview") + .HasColumnType("TEXT"); + + b.Property("PosterPath") + .HasColumnType("TEXT"); + + b.Property("QualityOverride") + .HasColumnType("INTEGER"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("RootFolder") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TotalSeasons") + .HasColumnType("INTEGER"); + + b.Property("TvDbId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Token") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Agent") + .HasColumnType("INTEGER"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("RadarrQualityProfile") + .HasColumnType("INTEGER"); + + b.Property("RadarrRootPath") + .HasColumnType("INTEGER"); + + b.Property("SonarrQualityProfile") + .HasColumnType("INTEGER"); + + b.Property("SonarrQualityProfileAnime") + .HasColumnType("INTEGER"); + + b.Property("SonarrRootPath") + .HasColumnType("INTEGER"); + + b.Property("SonarrRootPathAnime") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("Deleted") + .HasColumnType("INTEGER"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("VoteType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AirDate") + .HasColumnType("TEXT"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("Available") + .HasColumnType("INTEGER"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("Requested") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChildRequestId") + .HasColumnType("INTEGER"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ombi.Store.Entities.MobileDevices", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("ParentRequest"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Issues"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", null) + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests", null) + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + + b.Navigation("IssueCategory"); + + b.Navigation("UserReported"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildRequest"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Navigation("NotificationUserIds"); + + b.Navigation("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Navigation("Issues"); + + b.Navigation("SeasonRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Navigation("Comments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Navigation("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Navigation("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Navigation("Episodes"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiSqlite/20201211042424_Jellyfin.cs b/src/Ombi.Store/Migrations/OmbiSqlite/20201211042424_Jellyfin.cs new file mode 100644 index 000000000..4e9187237 --- /dev/null +++ b/src/Ombi.Store/Migrations/OmbiSqlite/20201211042424_Jellyfin.cs @@ -0,0 +1,69 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations.OmbiSqlite +{ + public partial class Jellyfin : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "JellyfinContent", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + AddedAt = table.Column(type: "TEXT", nullable: false), + JellyfinId = table.Column(type: "TEXT", nullable: false), + ProviderId = table.Column(type: "TEXT", nullable: true), + Title = table.Column(type: "TEXT", nullable: true), + Type = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_JellyfinContent", x => x.Id); + table.UniqueConstraint("AK_JellyfinContent_JellyfinId", x => x.JellyfinId); + }); + + migrationBuilder.CreateTable( + name: "JellyfinEpisode", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + AddedAt = table.Column(type: "TEXT", nullable: false), + JellyfinId = table.Column(type: "TEXT", nullable: true), + EpisodeNumber = table.Column(type: "INTEGER", nullable: false), + ParentId = table.Column(type: "TEXT", nullable: true), + ProviderId = table.Column(type: "TEXT", nullable: true), + SeasonNumber = table.Column(type: "INTEGER", nullable: false), + Title = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_JellyfinEpisode", x => x.Id); + table.ForeignKey( + name: "FK_JellyfinEpisode_JellyfinContent_ParentId", + column: x => x.ParentId, + principalTable: "JellyfinContent", + principalColumn: "JellyfinId", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_JellyfinEpisode_ParentId", + table: "JellyfinEpisode", + column: "ParentId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "JellyfinContent"); + + migrationBuilder.DropTable( + name: "JellyfinEpisode"); + } + } +} From ce9279a108a0990d4bd7618646c10b50dca31de4 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Fri, 11 Dec 2020 19:29:07 -0500 Subject: [PATCH 36/82] Add missing field for Jellyfin users --- .../usermanagement/usermanagement.component.html | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Ombi/ClientApp/src/app/settings/usermanagement/usermanagement.component.html b/src/Ombi/ClientApp/src/app/settings/usermanagement/usermanagement.component.html index 870bd45ae..bd259aff6 100644 --- a/src/Ombi/ClientApp/src/app/settings/usermanagement/usermanagement.component.html +++ b/src/Ombi/ClientApp/src/app/settings/usermanagement/usermanagement.component.html @@ -35,6 +35,18 @@ +
+
+ Import Jellyfin Users +
+ +
+

Jellyfin Users excluded from Import

+ + +
+ +

Default Roles

@@ -74,4 +86,4 @@
- \ No newline at end of file + From 0812a3f780d0a6683a9e1cbb42d70f4490f4e8cf Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Fri, 11 Dec 2020 20:38:07 -0500 Subject: [PATCH 37/82] Add more detailed migration documentation --- src/Ombi.Store/Migration.md | 50 ++++++++++++++++++++++++++++++++++++ src/Ombi.Store/Migration.txt | 3 --- 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 src/Ombi.Store/Migration.md delete mode 100644 src/Ombi.Store/Migration.txt diff --git a/src/Ombi.Store/Migration.md b/src/Ombi.Store/Migration.md new file mode 100644 index 000000000..5a8f9b667 --- /dev/null +++ b/src/Ombi.Store/Migration.md @@ -0,0 +1,50 @@ +``` +dotnet ef migrations add Inital --context OmbiSqliteContext --startup-project ../Ombi/Ombi.csproj +``` + +If running migrations for any db provider other than Sqlite, then ensure the database.json is pointing at the correct DB type + + +## More detailed explanation + +1. Install dotnet-ef, and include it in your $PATH if necessary: + + ``` + dotnet tool install --global dotnet-ef + export PATH="$HOME/.dotnet/tools:$PATH" + ``` + +1. In `src/Ombi`, install the `Microsoft.EntityFrameworkCore.Design` package: + + ``` + cd src/Ombi + dotnet add package Microsoft.EntityFrameworkCore.Design + ``` + +1. For some reason, the `StartupSingleton.Instance.SecurityKey` in `src/Ombi/Extensions/StartupExtensions.cs` is invalid when running `otnet ef migrations add` so we must fix it; apply this patch which seems to do the job: + + ``` + @@ -79,7 +79,7 @@ namespace Ombi + var tokenValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + - IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(StartupSingleton.Instance.SecurityKey)), + + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(StartupSingleton.Instance.SecurityKey + "s")), + RequireExpirationTime = true, + ValidateLifetime = true, + ValidAudience = "Ombi", + ``` + +1. List the available `dbcontext`s, and select the one that matches the database your fields will go in: + + ``` + cd src/Ombi.Store + dotnet ef dbcontext list + ``` + +1. Run the migration using the command at the start of this document: + + ``` + cd src/Ombi.Store + dotnet ef migrations add --context --startup-project ../Ombi/Ombi.csproj + ``` diff --git a/src/Ombi.Store/Migration.txt b/src/Ombi.Store/Migration.txt deleted file mode 100644 index 331299143..000000000 --- a/src/Ombi.Store/Migration.txt +++ /dev/null @@ -1,3 +0,0 @@ -dotnet ef migrations add Inital --context OmbiSqliteContext --startup-project ../Ombi/Ombi.csproj - -If running migrations for any db provider other than Sqlite, then ensure the database.json is pointing at the correct DB type \ No newline at end of file From 0fdfab19ddeb535c25919f140f23c3baed3150fb Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Fri, 11 Dec 2020 20:43:55 -0500 Subject: [PATCH 38/82] Move migration to proper database --- src/Ombi.Store/Migration.md | 2 +- .../20201212014227_Jellyfin.Designer.cs | 506 +++++++ .../20201212014227_Jellyfin.cs} | 2 +- .../ExternalSqliteContextModelSnapshot.cs | 354 +++-- .../20201211042424_Jellyfin.Designer.cs | 1221 ----------------- 5 files changed, 781 insertions(+), 1304 deletions(-) create mode 100644 src/Ombi.Store/Migrations/ExternalSqlite/20201212014227_Jellyfin.Designer.cs rename src/Ombi.Store/Migrations/{OmbiSqlite/20201211042424_Jellyfin.cs => ExternalSqlite/20201212014227_Jellyfin.cs} (98%) delete mode 100644 src/Ombi.Store/Migrations/OmbiSqlite/20201211042424_Jellyfin.Designer.cs diff --git a/src/Ombi.Store/Migration.md b/src/Ombi.Store/Migration.md index 5a8f9b667..8332e8ce9 100644 --- a/src/Ombi.Store/Migration.md +++ b/src/Ombi.Store/Migration.md @@ -21,7 +21,7 @@ If running migrations for any db provider other than Sqlite, then ensure the dat dotnet add package Microsoft.EntityFrameworkCore.Design ``` -1. For some reason, the `StartupSingleton.Instance.SecurityKey` in `src/Ombi/Extensions/StartupExtensions.cs` is invalid when running `otnet ef migrations add` so we must fix it; apply this patch which seems to do the job: +1. For some reason, the `StartupSingleton.Instance.SecurityKey` in `src/Ombi/Extensions/StartupExtensions.cs` is invalid when running `dotnet ef migrations add` so we must fix it; apply this patch which seems to do the job: ``` @@ -79,7 +79,7 @@ namespace Ombi diff --git a/src/Ombi.Store/Migrations/ExternalSqlite/20201212014227_Jellyfin.Designer.cs b/src/Ombi.Store/Migrations/ExternalSqlite/20201212014227_Jellyfin.Designer.cs new file mode 100644 index 000000000..19189c2e0 --- /dev/null +++ b/src/Ombi.Store/Migrations/ExternalSqlite/20201212014227_Jellyfin.Designer.cs @@ -0,0 +1,506 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context.Sqlite; + +namespace Ombi.Store.Migrations.ExternalSqlite +{ + [DbContext(typeof(ExternalSqliteContext))] + [Migration("20201212014227_Jellyfin")] + partial class Jellyfin + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.1"); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("TheMovieDbId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("EmbyId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("TheMovieDbId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TvDbId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("EmbyId") + .HasColumnType("TEXT"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("TheMovieDbId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TvDbId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("JellyfinId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("TheMovieDbId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TvDbId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("JellyfinContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("JellyfinId") + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("TheMovieDbId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TvDbId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("JellyfinEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("ArtistId") + .HasColumnType("INTEGER"); + + b.Property("ForeignAlbumId") + .HasColumnType("TEXT"); + + b.Property("Monitored") + .HasColumnType("INTEGER"); + + b.Property("PercentOfTracks") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TrackCount") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArtistId") + .HasColumnType("INTEGER"); + + b.Property("ArtistName") + .HasColumnType("TEXT"); + + b.Property("ForeignArtistId") + .HasColumnType("TEXT"); + + b.Property("Monitored") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("GrandparentKey") + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("INTEGER"); + + b.Property("ParentKey") + .HasColumnType("INTEGER"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ParentKey") + .HasColumnType("INTEGER"); + + b.Property("PlexContentId") + .HasColumnType("INTEGER"); + + b.Property("PlexServerContentId") + .HasColumnType("INTEGER"); + + b.Property("SeasonKey") + .HasColumnType("INTEGER"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("INTEGER"); + + b.Property("Quality") + .HasColumnType("TEXT"); + + b.Property("ReleaseYear") + .HasColumnType("TEXT"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("TheMovieDbId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TvDbId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("HasFile") + .HasColumnType("INTEGER"); + + b.Property("TheMovieDbId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("TvDbId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("TvDbId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("TvDbId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("HasFile") + .HasColumnType("INTEGER"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("TvDbId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + + b.Navigation("Series"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b => + { + b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("JellyfinId"); + + b.Navigation("Series"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Series"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", null) + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Navigation("Episodes"); + + b.Navigation("Seasons"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiSqlite/20201211042424_Jellyfin.cs b/src/Ombi.Store/Migrations/ExternalSqlite/20201212014227_Jellyfin.cs similarity index 98% rename from src/Ombi.Store/Migrations/OmbiSqlite/20201211042424_Jellyfin.cs rename to src/Ombi.Store/Migrations/ExternalSqlite/20201212014227_Jellyfin.cs index 4e9187237..50d630b16 100644 --- a/src/Ombi.Store/Migrations/OmbiSqlite/20201211042424_Jellyfin.cs +++ b/src/Ombi.Store/Migrations/ExternalSqlite/20201212014227_Jellyfin.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace Ombi.Store.Migrations.OmbiSqlite +namespace Ombi.Store.Migrations.ExternalSqlite { public partial class Jellyfin : Migration { diff --git a/src/Ombi.Store/Migrations/ExternalSqlite/ExternalSqliteContextModelSnapshot.cs b/src/Ombi.Store/Migrations/ExternalSqlite/ExternalSqliteContextModelSnapshot.cs index 86e292e2f..d8378c814 100644 --- a/src/Ombi.Store/Migrations/ExternalSqlite/ExternalSqliteContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/ExternalSqlite/ExternalSqliteContextModelSnapshot.cs @@ -14,14 +14,16 @@ namespace Ombi.Store.Migrations.ExternalSqlite { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); + .HasAnnotation("ProductVersion", "5.0.1"); modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); - b.Property("TheMovieDbId"); + b.Property("TheMovieDbId") + .HasColumnType("INTEGER"); b.HasKey("Id"); @@ -31,26 +33,36 @@ namespace Ombi.Store.Migrations.ExternalSqlite modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); - b.Property("AddedAt"); + b.Property("AddedAt") + .HasColumnType("TEXT"); b.Property("EmbyId") - .IsRequired(); + .IsRequired() + .HasColumnType("TEXT"); - b.Property("ImdbId"); + b.Property("ImdbId") + .HasColumnType("TEXT"); - b.Property("ProviderId"); + b.Property("ProviderId") + .HasColumnType("TEXT"); - b.Property("TheMovieDbId"); + b.Property("TheMovieDbId") + .HasColumnType("TEXT"); - b.Property("Title"); + b.Property("Title") + .HasColumnType("TEXT"); - b.Property("TvDbId"); + b.Property("TvDbId") + .HasColumnType("TEXT"); - b.Property("Type"); + b.Property("Type") + .HasColumnType("INTEGER"); - b.Property("Url"); + b.Property("Url") + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -60,27 +72,38 @@ namespace Ombi.Store.Migrations.ExternalSqlite modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); - b.Property("AddedAt"); + b.Property("AddedAt") + .HasColumnType("TEXT"); - b.Property("EmbyId"); + b.Property("EmbyId") + .HasColumnType("TEXT"); - b.Property("EpisodeNumber"); + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); - b.Property("ImdbId"); + b.Property("ImdbId") + .HasColumnType("TEXT"); - b.Property("ParentId"); + b.Property("ParentId") + .HasColumnType("TEXT"); - b.Property("ProviderId"); + b.Property("ProviderId") + .HasColumnType("TEXT"); - b.Property("SeasonNumber"); + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); - b.Property("TheMovieDbId"); + b.Property("TheMovieDbId") + .HasColumnType("TEXT"); - b.Property("Title"); + b.Property("Title") + .HasColumnType("TEXT"); - b.Property("TvDbId"); + b.Property("TvDbId") + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -89,26 +112,117 @@ namespace Ombi.Store.Migrations.ExternalSqlite b.ToTable("EmbyEpisode"); }); + modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("JellyfinId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("TheMovieDbId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TvDbId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("JellyfinContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("JellyfinId") + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("TheMovieDbId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TvDbId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("JellyfinEpisode"); + }); + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); - b.Property("AddedAt"); + b.Property("AddedAt") + .HasColumnType("TEXT"); - b.Property("ArtistId"); + b.Property("ArtistId") + .HasColumnType("INTEGER"); - b.Property("ForeignAlbumId"); + b.Property("ForeignAlbumId") + .HasColumnType("TEXT"); - b.Property("Monitored"); + b.Property("Monitored") + .HasColumnType("INTEGER"); - b.Property("PercentOfTracks"); + b.Property("PercentOfTracks") + .HasColumnType("TEXT"); - b.Property("ReleaseDate"); + b.Property("ReleaseDate") + .HasColumnType("TEXT"); - b.Property("Title"); + b.Property("Title") + .HasColumnType("TEXT"); - b.Property("TrackCount"); + b.Property("TrackCount") + .HasColumnType("INTEGER"); b.HasKey("Id"); @@ -118,15 +232,20 @@ namespace Ombi.Store.Migrations.ExternalSqlite modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); - b.Property("ArtistId"); + b.Property("ArtistId") + .HasColumnType("INTEGER"); - b.Property("ArtistName"); + b.Property("ArtistName") + .HasColumnType("TEXT"); - b.Property("ForeignArtistId"); + b.Property("ForeignArtistId") + .HasColumnType("TEXT"); - b.Property("Monitored"); + b.Property("Monitored") + .HasColumnType("INTEGER"); b.HasKey("Id"); @@ -136,19 +255,26 @@ namespace Ombi.Store.Migrations.ExternalSqlite modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); - b.Property("EpisodeNumber"); + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); - b.Property("GrandparentKey"); + b.Property("GrandparentKey") + .HasColumnType("INTEGER"); - b.Property("Key"); + b.Property("Key") + .HasColumnType("INTEGER"); - b.Property("ParentKey"); + b.Property("ParentKey") + .HasColumnType("INTEGER"); - b.Property("SeasonNumber"); + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); - b.Property("Title"); + b.Property("Title") + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -160,17 +286,23 @@ namespace Ombi.Store.Migrations.ExternalSqlite modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); - b.Property("ParentKey"); + b.Property("ParentKey") + .HasColumnType("INTEGER"); - b.Property("PlexContentId"); + b.Property("PlexContentId") + .HasColumnType("INTEGER"); - b.Property("PlexServerContentId"); + b.Property("PlexServerContentId") + .HasColumnType("INTEGER"); - b.Property("SeasonKey"); + b.Property("SeasonKey") + .HasColumnType("INTEGER"); - b.Property("SeasonNumber"); + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); b.HasKey("Id"); @@ -182,29 +314,41 @@ namespace Ombi.Store.Migrations.ExternalSqlite modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); - b.Property("AddedAt"); + b.Property("AddedAt") + .HasColumnType("TEXT"); - b.Property("ImdbId"); + b.Property("ImdbId") + .HasColumnType("TEXT"); - b.Property("Key"); + b.Property("Key") + .HasColumnType("INTEGER"); - b.Property("Quality"); + b.Property("Quality") + .HasColumnType("TEXT"); - b.Property("ReleaseYear"); + b.Property("ReleaseYear") + .HasColumnType("TEXT"); - b.Property("RequestId"); + b.Property("RequestId") + .HasColumnType("INTEGER"); - b.Property("TheMovieDbId"); + b.Property("TheMovieDbId") + .HasColumnType("TEXT"); - b.Property("Title"); + b.Property("Title") + .HasColumnType("TEXT"); - b.Property("TvDbId"); + b.Property("TvDbId") + .HasColumnType("TEXT"); - b.Property("Type"); + b.Property("Type") + .HasColumnType("INTEGER"); - b.Property("Url"); + b.Property("Url") + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -214,11 +358,14 @@ namespace Ombi.Store.Migrations.ExternalSqlite modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); - b.Property("HasFile"); + b.Property("HasFile") + .HasColumnType("INTEGER"); - b.Property("TheMovieDbId"); + b.Property("TheMovieDbId") + .HasColumnType("INTEGER"); b.HasKey("Id"); @@ -228,9 +375,11 @@ namespace Ombi.Store.Migrations.ExternalSqlite modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); - b.Property("TvDbId"); + b.Property("TvDbId") + .HasColumnType("INTEGER"); b.HasKey("Id"); @@ -240,13 +389,17 @@ namespace Ombi.Store.Migrations.ExternalSqlite modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); - b.Property("EpisodeNumber"); + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); - b.Property("SeasonNumber"); + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); - b.Property("TvDbId"); + b.Property("TvDbId") + .HasColumnType("INTEGER"); b.HasKey("Id"); @@ -256,9 +409,11 @@ namespace Ombi.Store.Migrations.ExternalSqlite modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); - b.Property("TvDbId"); + b.Property("TvDbId") + .HasColumnType("INTEGER"); b.HasKey("Id"); @@ -268,15 +423,20 @@ namespace Ombi.Store.Migrations.ExternalSqlite modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); - b.Property("EpisodeNumber"); + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); - b.Property("HasFile"); + b.Property("HasFile") + .HasColumnType("INTEGER"); - b.Property("SeasonNumber"); + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); - b.Property("TvDbId"); + b.Property("TvDbId") + .HasColumnType("INTEGER"); b.HasKey("Id"); @@ -289,6 +449,18 @@ namespace Ombi.Store.Migrations.ExternalSqlite .WithMany("Episodes") .HasForeignKey("ParentId") .HasPrincipalKey("EmbyId"); + + b.Navigation("Series"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b => + { + b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("JellyfinId"); + + b.Navigation("Series"); }); modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => @@ -297,15 +469,35 @@ namespace Ombi.Store.Migrations.ExternalSqlite .WithMany("Episodes") .HasForeignKey("GrandparentKey") .HasPrincipalKey("Key") - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Series"); }); modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => { - b.HasOne("Ombi.Store.Entities.PlexServerContent") + b.HasOne("Ombi.Store.Entities.PlexServerContent", null) .WithMany("Seasons") .HasForeignKey("PlexServerContentId"); }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Navigation("Episodes"); + + b.Navigation("Seasons"); + }); #pragma warning restore 612, 618 } } diff --git a/src/Ombi.Store/Migrations/OmbiSqlite/20201211042424_Jellyfin.Designer.cs b/src/Ombi.Store/Migrations/OmbiSqlite/20201211042424_Jellyfin.Designer.cs deleted file mode 100644 index 4fc5f9456..000000000 --- a/src/Ombi.Store/Migrations/OmbiSqlite/20201211042424_Jellyfin.Designer.cs +++ /dev/null @@ -1,1221 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Ombi.Store.Context.Sqlite; - -namespace Ombi.Store.Migrations.OmbiSqlite -{ - [DbContext(typeof(OmbiSqliteContext))] - [Migration("20201211042424_Jellyfin")] - partial class Jellyfin - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "5.0.1"); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("TEXT"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("AspNetRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ClaimType") - .HasColumnType("TEXT"); - - b.Property("ClaimValue") - .HasColumnType("TEXT"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ClaimType") - .HasColumnType("TEXT"); - - b.Property("ClaimValue") - .HasColumnType("TEXT"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("TEXT"); - - b.Property("ProviderKey") - .HasColumnType("TEXT"); - - b.Property("ProviderDisplayName") - .HasColumnType("TEXT"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("TEXT"); - - b.Property("RoleId") - .HasColumnType("TEXT"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("TEXT"); - - b.Property("LoginProvider") - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("Value") - .HasColumnType("TEXT"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Audit", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("AuditArea") - .HasColumnType("INTEGER"); - - b.Property("AuditType") - .HasColumnType("INTEGER"); - - b.Property("DateTime") - .HasColumnType("TEXT"); - - b.Property("Description") - .HasColumnType("TEXT"); - - b.Property("User") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Audit"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.MobileDevices", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("AddedAt") - .HasColumnType("TEXT"); - - b.Property("Token") - .HasColumnType("TEXT"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("MobileDevices"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Agent") - .HasColumnType("INTEGER"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("Message") - .HasColumnType("TEXT"); - - b.Property("NotificationType") - .HasColumnType("INTEGER"); - - b.Property("Subject") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("NotificationTemplates"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("AddedAt") - .HasColumnType("TEXT"); - - b.Property("PlayerId") - .HasColumnType("TEXT"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("NotificationUserId"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => - { - b.Property("Id") - .HasColumnType("TEXT"); - - b.Property("AccessFailedCount") - .HasColumnType("INTEGER"); - - b.Property("Alias") - .HasColumnType("TEXT"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("TEXT"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("EmailConfirmed") - .HasColumnType("INTEGER"); - - b.Property("EpisodeRequestLimit") - .HasColumnType("INTEGER"); - - b.Property("Language") - .HasColumnType("TEXT"); - - b.Property("LastLoggedIn") - .HasColumnType("TEXT"); - - b.Property("LockoutEnabled") - .HasColumnType("INTEGER"); - - b.Property("LockoutEnd") - .HasColumnType("TEXT"); - - b.Property("MovieRequestLimit") - .HasColumnType("INTEGER"); - - b.Property("MusicRequestLimit") - .HasColumnType("INTEGER"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("PasswordHash") - .HasColumnType("TEXT"); - - b.Property("PhoneNumber") - .HasColumnType("TEXT"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("INTEGER"); - - b.Property("ProviderUserId") - .HasColumnType("TEXT"); - - b.Property("SecurityStamp") - .HasColumnType("TEXT"); - - b.Property("TwoFactorEnabled") - .HasColumnType("INTEGER"); - - b.Property("UserAccessToken") - .HasColumnType("TEXT"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("UserType") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.ToTable("AspNetUsers"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("AddedAt") - .HasColumnType("TEXT"); - - b.Property("AlbumId") - .HasColumnType("TEXT"); - - b.Property("ContentId") - .HasColumnType("INTEGER"); - - b.Property("ContentType") - .HasColumnType("INTEGER"); - - b.Property("EpisodeNumber") - .HasColumnType("INTEGER"); - - b.Property("SeasonNumber") - .HasColumnType("INTEGER"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("RecentlyAddedLog"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.RequestQueue", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Completed") - .HasColumnType("TEXT"); - - b.Property("Dts") - .HasColumnType("TEXT"); - - b.Property("Error") - .HasColumnType("TEXT"); - - b.Property("RequestId") - .HasColumnType("INTEGER"); - - b.Property("RetryCount") - .HasColumnType("INTEGER"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("RequestQueue"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("RequestId") - .HasColumnType("INTEGER"); - - b.Property("RequestType") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("RequestSubscription"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Approved") - .HasColumnType("INTEGER"); - - b.Property("ArtistName") - .HasColumnType("TEXT"); - - b.Property("Available") - .HasColumnType("INTEGER"); - - b.Property("Cover") - .HasColumnType("TEXT"); - - b.Property("Denied") - .HasColumnType("INTEGER"); - - b.Property("DeniedReason") - .HasColumnType("TEXT"); - - b.Property("Disk") - .HasColumnType("TEXT"); - - b.Property("ForeignAlbumId") - .HasColumnType("TEXT"); - - b.Property("ForeignArtistId") - .HasColumnType("TEXT"); - - b.Property("MarkedAsApproved") - .HasColumnType("TEXT"); - - b.Property("MarkedAsAvailable") - .HasColumnType("TEXT"); - - b.Property("MarkedAsDenied") - .HasColumnType("TEXT"); - - b.Property("Rating") - .HasColumnType("TEXT"); - - b.Property("ReleaseDate") - .HasColumnType("TEXT"); - - b.Property("RequestType") - .HasColumnType("INTEGER"); - - b.Property("RequestedByAlias") - .HasColumnType("TEXT"); - - b.Property("RequestedDate") - .HasColumnType("TEXT"); - - b.Property("RequestedUserId") - .HasColumnType("TEXT"); - - b.Property("Title") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("RequestedUserId"); - - b.ToTable("AlbumRequests"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Approved") - .HasColumnType("INTEGER"); - - b.Property("Available") - .HasColumnType("INTEGER"); - - b.Property("Denied") - .HasColumnType("INTEGER"); - - b.Property("DeniedReason") - .HasColumnType("TEXT"); - - b.Property("IssueId") - .HasColumnType("INTEGER"); - - b.Property("MarkedAsApproved") - .HasColumnType("TEXT"); - - b.Property("MarkedAsAvailable") - .HasColumnType("TEXT"); - - b.Property("MarkedAsDenied") - .HasColumnType("TEXT"); - - b.Property("ParentRequestId") - .HasColumnType("INTEGER"); - - b.Property("RequestType") - .HasColumnType("INTEGER"); - - b.Property("RequestedByAlias") - .HasColumnType("TEXT"); - - b.Property("RequestedDate") - .HasColumnType("TEXT"); - - b.Property("RequestedUserId") - .HasColumnType("TEXT"); - - b.Property("SeriesType") - .HasColumnType("INTEGER"); - - b.Property("Title") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ParentRequestId"); - - b.HasIndex("RequestedUserId"); - - b.ToTable("ChildRequests"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Value") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("IssueCategory"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Comment") - .HasColumnType("TEXT"); - - b.Property("Date") - .HasColumnType("TEXT"); - - b.Property("IssuesId") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("IssuesId"); - - b.HasIndex("UserId"); - - b.ToTable("IssueComments"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CreatedDate") - .HasColumnType("TEXT"); - - b.Property("Description") - .HasColumnType("TEXT"); - - b.Property("IssueCategoryId") - .HasColumnType("INTEGER"); - - b.Property("IssueId") - .HasColumnType("INTEGER"); - - b.Property("ProviderId") - .HasColumnType("TEXT"); - - b.Property("RequestId") - .HasColumnType("INTEGER"); - - b.Property("RequestType") - .HasColumnType("INTEGER"); - - b.Property("ResovledDate") - .HasColumnType("TEXT"); - - b.Property("Status") - .HasColumnType("INTEGER"); - - b.Property("Subject") - .HasColumnType("TEXT"); - - b.Property("Title") - .HasColumnType("TEXT"); - - b.Property("UserReportedId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("IssueCategoryId"); - - b.HasIndex("IssueId"); - - b.HasIndex("UserReportedId"); - - b.ToTable("Issues"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Approved") - .HasColumnType("INTEGER"); - - b.Property("Available") - .HasColumnType("INTEGER"); - - b.Property("Background") - .HasColumnType("TEXT"); - - b.Property("Denied") - .HasColumnType("INTEGER"); - - b.Property("DeniedReason") - .HasColumnType("TEXT"); - - b.Property("DigitalReleaseDate") - .HasColumnType("TEXT"); - - b.Property("ImdbId") - .HasColumnType("TEXT"); - - b.Property("IssueId") - .HasColumnType("INTEGER"); - - b.Property("LangCode") - .HasColumnType("TEXT"); - - b.Property("MarkedAsApproved") - .HasColumnType("TEXT"); - - b.Property("MarkedAsAvailable") - .HasColumnType("TEXT"); - - b.Property("MarkedAsDenied") - .HasColumnType("TEXT"); - - b.Property("Overview") - .HasColumnType("TEXT"); - - b.Property("PosterPath") - .HasColumnType("TEXT"); - - b.Property("QualityOverride") - .HasColumnType("INTEGER"); - - b.Property("ReleaseDate") - .HasColumnType("TEXT"); - - b.Property("RequestType") - .HasColumnType("INTEGER"); - - b.Property("RequestedByAlias") - .HasColumnType("TEXT"); - - b.Property("RequestedDate") - .HasColumnType("TEXT"); - - b.Property("RequestedUserId") - .HasColumnType("TEXT"); - - b.Property("RootPathOverride") - .HasColumnType("INTEGER"); - - b.Property("Status") - .HasColumnType("TEXT"); - - b.Property("TheMovieDbId") - .HasColumnType("INTEGER"); - - b.Property("Title") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("RequestedUserId"); - - b.ToTable("MovieRequests"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("EpisodeCount") - .HasColumnType("INTEGER"); - - b.Property("RequestDate") - .HasColumnType("TEXT"); - - b.Property("RequestId") - .HasColumnType("INTEGER"); - - b.Property("RequestType") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("RequestLog"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Background") - .HasColumnType("TEXT"); - - b.Property("ImdbId") - .HasColumnType("TEXT"); - - b.Property("Overview") - .HasColumnType("TEXT"); - - b.Property("PosterPath") - .HasColumnType("TEXT"); - - b.Property("QualityOverride") - .HasColumnType("INTEGER"); - - b.Property("ReleaseDate") - .HasColumnType("TEXT"); - - b.Property("RootFolder") - .HasColumnType("INTEGER"); - - b.Property("Status") - .HasColumnType("TEXT"); - - b.Property("Title") - .HasColumnType("TEXT"); - - b.Property("TotalSeasons") - .HasColumnType("INTEGER"); - - b.Property("TvDbId") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("TvRequests"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Token") - .HasColumnType("TEXT"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Tokens"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Agent") - .HasColumnType("INTEGER"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.Property("Value") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("UserNotificationPreferences"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("RadarrQualityProfile") - .HasColumnType("INTEGER"); - - b.Property("RadarrRootPath") - .HasColumnType("INTEGER"); - - b.Property("SonarrQualityProfile") - .HasColumnType("INTEGER"); - - b.Property("SonarrQualityProfileAnime") - .HasColumnType("INTEGER"); - - b.Property("SonarrRootPath") - .HasColumnType("INTEGER"); - - b.Property("SonarrRootPathAnime") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("UserQualityProfiles"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Votes", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Date") - .HasColumnType("TEXT"); - - b.Property("Deleted") - .HasColumnType("INTEGER"); - - b.Property("RequestId") - .HasColumnType("INTEGER"); - - b.Property("RequestType") - .HasColumnType("INTEGER"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.Property("VoteType") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Votes"); - }); - - modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("AirDate") - .HasColumnType("TEXT"); - - b.Property("Approved") - .HasColumnType("INTEGER"); - - b.Property("Available") - .HasColumnType("INTEGER"); - - b.Property("EpisodeNumber") - .HasColumnType("INTEGER"); - - b.Property("Requested") - .HasColumnType("INTEGER"); - - b.Property("SeasonId") - .HasColumnType("INTEGER"); - - b.Property("Title") - .HasColumnType("TEXT"); - - b.Property("Url") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("SeasonId"); - - b.ToTable("EpisodeRequests"); - }); - - modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ChildRequestId") - .HasColumnType("INTEGER"); - - b.Property("SeasonNumber") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("ChildRequestId"); - - b.ToTable("SeasonRequests"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Ombi.Store.Entities.OmbiUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Ombi.Store.Entities.OmbiUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Ombi.Store.Entities.OmbiUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Ombi.Store.Entities.OmbiUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Ombi.Store.Entities.MobileDevices", b => - { - b.HasOne("Ombi.Store.Entities.OmbiUser", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => - { - b.HasOne("Ombi.Store.Entities.OmbiUser", "User") - .WithMany("NotificationUserIds") - .HasForeignKey("UserId"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => - { - b.HasOne("Ombi.Store.Entities.OmbiUser", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => - { - b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") - .WithMany() - .HasForeignKey("RequestedUserId"); - - b.Navigation("RequestedUser"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => - { - b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") - .WithMany("ChildRequests") - .HasForeignKey("ParentRequestId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") - .WithMany() - .HasForeignKey("RequestedUserId"); - - b.Navigation("ParentRequest"); - - b.Navigation("RequestedUser"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => - { - b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") - .WithMany("Comments") - .HasForeignKey("IssuesId"); - - b.HasOne("Ombi.Store.Entities.OmbiUser", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Issues"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => - { - b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") - .WithMany() - .HasForeignKey("IssueCategoryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", null) - .WithMany("Issues") - .HasForeignKey("IssueId"); - - b.HasOne("Ombi.Store.Entities.Requests.MovieRequests", null) - .WithMany("Issues") - .HasForeignKey("IssueId"); - - b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") - .WithMany() - .HasForeignKey("UserReportedId"); - - b.Navigation("IssueCategory"); - - b.Navigation("UserReported"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => - { - b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") - .WithMany() - .HasForeignKey("RequestedUserId"); - - b.Navigation("RequestedUser"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => - { - b.HasOne("Ombi.Store.Entities.OmbiUser", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => - { - b.HasOne("Ombi.Store.Entities.OmbiUser", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => - { - b.HasOne("Ombi.Store.Entities.OmbiUser", "User") - .WithMany("UserNotificationPreferences") - .HasForeignKey("UserId"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => - { - b.HasOne("Ombi.Store.Entities.OmbiUser", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Votes", b => - { - b.HasOne("Ombi.Store.Entities.OmbiUser", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => - { - b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Season"); - }); - - modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => - { - b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") - .WithMany("SeasonRequests") - .HasForeignKey("ChildRequestId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ChildRequest"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => - { - b.Navigation("NotificationUserIds"); - - b.Navigation("UserNotificationPreferences"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => - { - b.Navigation("Issues"); - - b.Navigation("SeasonRequests"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => - { - b.Navigation("Comments"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => - { - b.Navigation("Issues"); - }); - - modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => - { - b.Navigation("ChildRequests"); - }); - - modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => - { - b.Navigation("Episodes"); - }); -#pragma warning restore 612, 618 - } - } -} From 635079155f2998be2d5acfdfe0f4429236ac7fe3 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Fri, 11 Dec 2020 20:57:29 -0500 Subject: [PATCH 39/82] Correct legends with proper names --- src/Ombi/ClientApp/src/app/settings/emby/emby.component.html | 2 +- .../src/app/settings/jellyfin/jellyfin.component.html | 2 +- src/Ombi/ClientApp/src/app/wizard/emby/emby.component.html | 4 ++-- .../ClientApp/src/app/wizard/jellyfin/jellyfin.component.html | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/settings/emby/emby.component.html b/src/Ombi/ClientApp/src/app/settings/emby/emby.component.html index e6d35b1c1..c1d8c1a83 100644 --- a/src/Ombi/ClientApp/src/app/settings/emby/emby.component.html +++ b/src/Ombi/ClientApp/src/app/settings/emby/emby.component.html @@ -4,7 +4,7 @@
- Emby/Jellyfin Configuration + Emby Configuration
diff --git a/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.html b/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.html index 00a0455e5..8eb1f90ed 100644 --- a/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.html +++ b/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.html @@ -4,7 +4,7 @@
- Jellyfin/Jellyfin Configuration + Jellyfin Configuration
diff --git a/src/Ombi/ClientApp/src/app/wizard/emby/emby.component.html b/src/Ombi/ClientApp/src/app/wizard/emby/emby.component.html index c5d99d5a4..9c92f8a86 100644 --- a/src/Ombi/ClientApp/src/app/wizard/emby/emby.component.html +++ b/src/Ombi/ClientApp/src/app/wizard/emby/emby.component.html @@ -4,7 +4,7 @@
- +
@@ -28,4 +28,4 @@
-
\ No newline at end of file +
diff --git a/src/Ombi/ClientApp/src/app/wizard/jellyfin/jellyfin.component.html b/src/Ombi/ClientApp/src/app/wizard/jellyfin/jellyfin.component.html index 38c220b04..f88342c1d 100644 --- a/src/Ombi/ClientApp/src/app/wizard/jellyfin/jellyfin.component.html +++ b/src/Ombi/ClientApp/src/app/wizard/jellyfin/jellyfin.component.html @@ -4,7 +4,7 @@
- +
@@ -28,4 +28,4 @@
- \ No newline at end of file + From f8b9b3ca259de62942ee16dc1a199ef55bfd6237 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Fri, 11 Dec 2020 23:09:48 -0500 Subject: [PATCH 40/82] Add warning about reverting patch --- src/Ombi.Store/Migration.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Ombi.Store/Migration.md b/src/Ombi.Store/Migration.md index 8332e8ce9..50711820b 100644 --- a/src/Ombi.Store/Migration.md +++ b/src/Ombi.Store/Migration.md @@ -35,6 +35,8 @@ If running migrations for any db provider other than Sqlite, then ensure the dat ValidAudience = "Ombi", ``` + *WARNING*: Don't forget to undo this before building Ombi, or things will be broken! + 1. List the available `dbcontext`s, and select the one that matches the database your fields will go in: ``` From 6a96d6718b948ff759f00c8bc9e743d0e2fc1a23 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Fri, 11 Dec 2020 23:18:45 -0500 Subject: [PATCH 41/82] Remove remaining IsJellyfin sections --- src/Ombi.Api.Emby/Models/PublicInfo.cs | 1 - src/Ombi.Api.Jellyfin/Models/PublicInfo.cs | 9 +-------- .../Rule/Rules/Search/EmbyAvailabilityRule.cs | 6 +++--- .../Rule/Rules/Search/JellyfinAvailabilityRule.cs | 4 ++-- src/Ombi.Helpers/EmbyHelper.cs | 6 +----- src/Ombi.Helpers/JellyfinHelper.cs | 10 +++------- src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs | 6 +++--- src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs | 4 ++-- src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs | 6 +++--- .../Jobs/Jellyfin/JellyfinUserImporter.cs | 2 +- .../Settings/Models/External/EmbySettings.cs | 3 +-- .../Settings/Models/External/JellyfinSettings.cs | 1 - src/Ombi/ClientApp/src/app/interfaces/ISettings.ts | 2 -- .../src/app/settings/emby/emby.component.html | 4 ++-- .../src/app/settings/jellyfin/jellyfin.component.html | 4 ++-- .../ClientApp/src/app/wizard/emby/emby.component.ts | 1 - .../src/app/wizard/jellyfin/jellyfin.component.ts | 1 - 17 files changed, 24 insertions(+), 46 deletions(-) diff --git a/src/Ombi.Api.Emby/Models/PublicInfo.cs b/src/Ombi.Api.Emby/Models/PublicInfo.cs index 75be0f172..23db412b5 100644 --- a/src/Ombi.Api.Emby/Models/PublicInfo.cs +++ b/src/Ombi.Api.Emby/Models/PublicInfo.cs @@ -5,7 +5,6 @@ public string LocalAddress { get; set; } public string ServerName { get; set; } public string Version { get; set; } - public string OperatingSystem { get; set; } public string Id { get; set; } } diff --git a/src/Ombi.Api.Jellyfin/Models/PublicInfo.cs b/src/Ombi.Api.Jellyfin/Models/PublicInfo.cs index 56ae605e2..6687cf3c9 100644 --- a/src/Ombi.Api.Jellyfin/Models/PublicInfo.cs +++ b/src/Ombi.Api.Jellyfin/Models/PublicInfo.cs @@ -5,15 +5,8 @@ public string LocalAddress { get; set; } public string ServerName { get; set; } public string Version { get; set; } - /// - /// Only populated for Jellyfin - /// - public string ProductName { get; set; } - - public bool IsJellyfin => !string.IsNullOrEmpty(ProductName) && ProductName.Contains("Jellyfin"); - public string OperatingSystem { get; set; } public string Id { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs b/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs index 75b6633bb..3fe11cbc4 100644 --- a/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs @@ -70,11 +70,11 @@ namespace Ombi.Core.Rule.Rules.Search var server = s.Servers.FirstOrDefault(x => x.ServerHostname != null); if ((server?.ServerHostname ?? string.Empty).HasValue()) { - obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, server?.ServerHostname, s.IsJellyfin); + obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, server?.ServerHostname); } else { - obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, null, s.IsJellyfin); + obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, null); } } @@ -100,4 +100,4 @@ namespace Ombi.Core.Rule.Rules.Search return Success(); } } -} \ No newline at end of file +} diff --git a/src/Ombi.Core/Rule/Rules/Search/JellyfinAvailabilityRule.cs b/src/Ombi.Core/Rule/Rules/Search/JellyfinAvailabilityRule.cs index c900844a3..2f6a39f91 100644 --- a/src/Ombi.Core/Rule/Rules/Search/JellyfinAvailabilityRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/JellyfinAvailabilityRule.cs @@ -70,11 +70,11 @@ namespace Ombi.Core.Rule.Rules.Search var server = s.Servers.FirstOrDefault(x => x.ServerHostname != null); if ((server?.ServerHostname ?? string.Empty).HasValue()) { - obj.JellyfinUrl = JellyfinHelper.GetJellyfinMediaUrl(item.JellyfinId, server?.ServerId, server?.ServerHostname, s.IsJellyfin); + obj.JellyfinUrl = JellyfinHelper.GetJellyfinMediaUrl(item.JellyfinId, server?.ServerId, server?.ServerHostname); } else { - obj.JellyfinUrl = JellyfinHelper.GetJellyfinMediaUrl(item.JellyfinId, server?.ServerId, null, s.IsJellyfin); + obj.JellyfinUrl = JellyfinHelper.GetJellyfinMediaUrl(item.JellyfinId, server?.ServerId, null); } } diff --git a/src/Ombi.Helpers/EmbyHelper.cs b/src/Ombi.Helpers/EmbyHelper.cs index 785ca47d4..db739b375 100644 --- a/src/Ombi.Helpers/EmbyHelper.cs +++ b/src/Ombi.Helpers/EmbyHelper.cs @@ -2,14 +2,10 @@ { public static class EmbyHelper { - public static string GetEmbyMediaUrl(string mediaId, string serverId, string customerServerUrl = null, bool isJellyfin = false) + public static string GetEmbyMediaUrl(string mediaId, string serverId, string customerServerUrl = null) { //web/index.html#!/details|item string path = "item"; - if (isJellyfin) - { - path = "details"; - } if (customerServerUrl.HasValue()) { if (!customerServerUrl.EndsWith("/")) diff --git a/src/Ombi.Helpers/JellyfinHelper.cs b/src/Ombi.Helpers/JellyfinHelper.cs index 7a342160e..506341d7d 100644 --- a/src/Ombi.Helpers/JellyfinHelper.cs +++ b/src/Ombi.Helpers/JellyfinHelper.cs @@ -2,14 +2,10 @@ { public static class JellyfinHelper { - public static string GetJellyfinMediaUrl(string mediaId, string serverId, string customerServerUrl = null, bool isJellyfin = false) + public static string GetJellyfinMediaUrl(string mediaId, string serverId, string customerServerUrl = null) { //web/index.html#!/details|item - string path = "item"; - if (isJellyfin) - { - path = "details"; - } + string path = "details"; if (customerServerUrl.HasValue()) { if (!customerServerUrl.EndsWith("/")) @@ -20,7 +16,7 @@ } else { - return $"https://app.jellyfin.media/web/index.html#!/{path}?id={mediaId}&serverId={serverId}"; + return $"http://localhost:8096/web/index.html#!/{path}?id={mediaId}&serverId={serverId}"; } } } diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs index f8bde2755..866216fe4 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs @@ -58,7 +58,7 @@ namespace Ombi.Schedule.Jobs.Emby { await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) .SendAsync(NotificationHub.NotificationEvent, "Emby Content Sync Failed"); - _logger.LogError(e, "Exception when caching {1} for server {0}", server.Name, embySettings.IsJellyfin ? "Jellyfin" : "Emby"); + _logger.LogError(e, "Exception when caching Emby for server {0}", server.Name); } } @@ -145,7 +145,7 @@ namespace Ombi.Schedule.Jobs.Emby Title = tvShow.Name, Type = EmbyMediaType.Series, EmbyId = tvShow.Id, - Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname, settings.IsJellyfin), + Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname), AddedAt = DateTime.UtcNow }); } @@ -228,4 +228,4 @@ namespace Ombi.Schedule.Jobs.Emby } } -} \ No newline at end of file +} diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs index 1207e1f42..13821f5d9 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs @@ -80,7 +80,7 @@ namespace Ombi.Schedule.Jobs.Emby Api = _apiFactory.CreateClient(settings); await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) - .SendAsync(NotificationHub.NotificationEvent, $"{(settings.IsJellyfin ? "Jellyfin" : "Emby")} User Importer Started"); + .SendAsync(NotificationHub.NotificationEvent, $"Emby User Importer Started"); var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.EmbyUser || x.UserType == UserType.EmbyConnectUser).ToListAsync(); foreach (var server in settings.Servers) { @@ -180,4 +180,4 @@ namespace Ombi.Schedule.Jobs.Emby GC.SuppressFinalize(this); } } -} \ No newline at end of file +} diff --git a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs index 2453f2008..ff96e2130 100644 --- a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinContentSync.cs @@ -58,7 +58,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin { await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) .SendAsync(NotificationHub.NotificationEvent, "Jellyfin Content Sync Failed"); - _logger.LogError(e, "Exception when caching {1} for server {0}", server.Name, jellyfinSettings.IsJellyfin ? "Jellyfin" : "Jellyfin"); + _logger.LogError(e, "Exception when caching Jellyfin for server {0}", server.Name); } } @@ -145,7 +145,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin Title = tvShow.Name, Type = JellyfinMediaType.Series, JellyfinId = tvShow.Id, - Url = JellyfinHelper.GetJellyfinMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname, settings.IsJellyfin), + Url = JellyfinHelper.GetJellyfinMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname), AddedAt = DateTime.UtcNow }); } @@ -228,4 +228,4 @@ namespace Ombi.Schedule.Jobs.Jellyfin } } -} \ No newline at end of file +} diff --git a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs index 2c39616ff..c7322daa9 100644 --- a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs +++ b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs @@ -80,7 +80,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin Api = _apiFactory.CreateClient(settings); await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) - .SendAsync(NotificationHub.NotificationEvent, $"{(settings.IsJellyfin ? "Jellyfin" : "Jellyfin")} User Importer Started"); + .SendAsync(NotificationHub.NotificationEvent, $"Jellyfin User Importer Started"); var allUsers = await _userManager.Users.Where(x => x.UserType == UserType.JellyfinUser).ToListAsync(); foreach (var server in settings.Servers) { diff --git a/src/Ombi.Settings/Settings/Models/External/EmbySettings.cs b/src/Ombi.Settings/Settings/Models/External/EmbySettings.cs index b3ffce0e1..5bd7cea93 100644 --- a/src/Ombi.Settings/Settings/Models/External/EmbySettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/EmbySettings.cs @@ -6,7 +6,6 @@ namespace Ombi.Core.Settings.Models.External public sealed class EmbySettings : Ombi.Settings.Settings.Models.Settings { public bool Enable { get; set; } - public bool IsJellyfin { get; set; } public List Servers { get; set; } = new List(); } @@ -19,4 +18,4 @@ namespace Ombi.Core.Settings.Models.External public string ServerHostname { get; set; } public bool EnableEpisodeSearching { get; set; } } -} \ No newline at end of file +} diff --git a/src/Ombi.Settings/Settings/Models/External/JellyfinSettings.cs b/src/Ombi.Settings/Settings/Models/External/JellyfinSettings.cs index 0785137c5..3bee56848 100644 --- a/src/Ombi.Settings/Settings/Models/External/JellyfinSettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/JellyfinSettings.cs @@ -6,7 +6,6 @@ namespace Ombi.Core.Settings.Models.External public sealed class JellyfinSettings : Ombi.Settings.Settings.Models.Settings { public bool Enable { get; set; } - public bool IsJellyfin { get; set; } public List Servers { get; set; } = new List(); } diff --git a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts index bcd0e7d4a..744148ff9 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts @@ -36,7 +36,6 @@ export interface IUpdateSettings extends ISettings { export interface IEmbySettings extends ISettings { enable: boolean; - isJellyfin: boolean; servers: IEmbyServer[]; } @@ -56,7 +55,6 @@ export interface IPublicInfo { export interface IJellyfinSettings extends ISettings { enable: boolean; - isJellyfin: boolean; servers: IJellyfinServer[]; } diff --git a/src/Ombi/ClientApp/src/app/settings/emby/emby.component.html b/src/Ombi/ClientApp/src/app/settings/emby/emby.component.html index c1d8c1a83..e7dd34504 100644 --- a/src/Ombi/ClientApp/src/app/settings/emby/emby.component.html +++ b/src/Ombi/ClientApp/src/app/settings/emby/emby.component.html @@ -70,8 +70,8 @@ - Current URL: "{{server.serverHostname}}/#!/{{settings.isJellyfin ? ("itemdetails"): ("item/item")}}.html?id=1" - Current URL: "https://app.emby.media/#!/{{settings.isJellyfin ? ("itemdetails"): ("item/item")}}.html?id=1 + Current URL: "{{server.serverHostname}}/#!/item/item.html?id=1" + Current URL: "https://app.emby.media/#!/item/item.html?id=1 diff --git a/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.html b/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.html index 8eb1f90ed..4ff8991c1 100644 --- a/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.html +++ b/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.html @@ -70,8 +70,8 @@ - Current URL: "{{server.serverHostname}}/#!/{{settings.isJellyfin ? ("itemdetails"): ("item/item")}}.html?id=1" - Current URL: "https://app.jellyfin.media/#!/{{settings.isJellyfin ? ("itemdetails"): ("item/item")}}.html?id=1 + Current URL: "{{server.serverHostname}}/#!/itemdetails.html?id=1" + Current URL: "https://app.jellyfin.media/#!/itemdetails.html?id=1 diff --git a/src/Ombi/ClientApp/src/app/wizard/emby/emby.component.ts b/src/Ombi/ClientApp/src/app/wizard/emby/emby.component.ts index 58d5dd3f6..f528d8be3 100644 --- a/src/Ombi/ClientApp/src/app/wizard/emby/emby.component.ts +++ b/src/Ombi/ClientApp/src/app/wizard/emby/emby.component.ts @@ -20,7 +20,6 @@ export class EmbyComponent implements OnInit { public ngOnInit() { this.embySettings = { servers: [], - isJellyfin: false, id: 0, enable: true, }; diff --git a/src/Ombi/ClientApp/src/app/wizard/jellyfin/jellyfin.component.ts b/src/Ombi/ClientApp/src/app/wizard/jellyfin/jellyfin.component.ts index 4c42c88ac..e6b96d0f7 100644 --- a/src/Ombi/ClientApp/src/app/wizard/jellyfin/jellyfin.component.ts +++ b/src/Ombi/ClientApp/src/app/wizard/jellyfin/jellyfin.component.ts @@ -20,7 +20,6 @@ export class JellyfinComponent implements OnInit { public ngOnInit() { this.jellyfinSettings = { servers: [], - isJellyfin: false, id: 0, enable: true, }; From fce9e88b1e783c8f6e5a165376ef462207e01b87 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sat, 12 Dec 2020 00:26:47 -0500 Subject: [PATCH 42/82] Fix broken migration --- .../ExternalSqlite/20201212014227_Jellyfin.cs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Ombi.Store/Migrations/ExternalSqlite/20201212014227_Jellyfin.cs b/src/Ombi.Store/Migrations/ExternalSqlite/20201212014227_Jellyfin.cs index 50d630b16..cd6d225d5 100644 --- a/src/Ombi.Store/Migrations/ExternalSqlite/20201212014227_Jellyfin.cs +++ b/src/Ombi.Store/Migrations/ExternalSqlite/20201212014227_Jellyfin.cs @@ -14,11 +14,15 @@ namespace Ombi.Store.Migrations.ExternalSqlite { Id = table.Column(type: "INTEGER", nullable: false) .Annotation("Sqlite:Autoincrement", true), - AddedAt = table.Column(type: "TEXT", nullable: false), - JellyfinId = table.Column(type: "TEXT", nullable: false), - ProviderId = table.Column(type: "TEXT", nullable: true), Title = table.Column(type: "TEXT", nullable: true), - Type = table.Column(type: "INTEGER", nullable: false) + ProviderId = table.Column(type: "TEXT", nullable: true), + JellyfinId = table.Column(type: "TEXT", nullable: false), + Type = table.Column(type: "INTEGER", nullable: false), + AddedAt = table.Column(type: "TEXT", nullable: false), + ImdbId = table.Column(type: "TEXT", nullable: true), + TheMovieDbId = table.Column(type: "TEXT", nullable: true), + TvDbId = table.Column(type: "TEXT", nullable: true), + Url = table.Column(type: "TEXT", nullable: true) }, constraints: table => { @@ -32,13 +36,16 @@ namespace Ombi.Store.Migrations.ExternalSqlite { Id = table.Column(type: "INTEGER", nullable: false) .Annotation("Sqlite:Autoincrement", true), - AddedAt = table.Column(type: "TEXT", nullable: false), + Title = table.Column(type: "TEXT", nullable: true), JellyfinId = table.Column(type: "TEXT", nullable: true), EpisodeNumber = table.Column(type: "INTEGER", nullable: false), + SeasonNumber = table.Column(type: "INTEGER", nullable: false), ParentId = table.Column(type: "TEXT", nullable: true), ProviderId = table.Column(type: "TEXT", nullable: true), - SeasonNumber = table.Column(type: "INTEGER", nullable: false), - Title = table.Column(type: "TEXT", nullable: true) + AddedAt = table.Column(type: "TEXT", nullable: false), + TvDbId = table.Column(type: "TEXT", nullable: true), + ImdbId = table.Column(type: "TEXT", nullable: true), + TheMovieDbId = table.Column(type: "TEXT", nullable: true) }, constraints: table => { From 85e98e587f9b8b4315b156ab767dc49be6d8bb0e Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sat, 12 Dec 2020 02:55:57 -0500 Subject: [PATCH 43/82] Correct JellyfinUser to type 5 Make everything consistent here with the Store entity number. --- src/Ombi.Core/Models/UserDto.cs | 2 +- src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs | 3 +-- src/Ombi/ClientApp/src/app/interfaces/IUser.ts | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Ombi.Core/Models/UserDto.cs b/src/Ombi.Core/Models/UserDto.cs index 6a8155bdb..7fbdb3465 100644 --- a/src/Ombi.Core/Models/UserDto.cs +++ b/src/Ombi.Core/Models/UserDto.cs @@ -20,6 +20,6 @@ namespace Ombi.Core.Models LocalUser = 1, PlexUser = 2, EmbyUser = 3, - JellyfinUser = 4 + JellyfinUser = 5 } } diff --git a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs index c7322daa9..d0d95ef3c 100644 --- a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs +++ b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs @@ -105,7 +105,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin if (!jellyfinUser.ConnectUserName.HasValue() && !jellyfinUser.Name.HasValue()) { - _log.LogInformation("Could not create Jellyfin user since the have no username, PlexUserId: {0}", jellyfinUser.Id); + _log.LogInformation("Could not create Jellyfin user since the have no username, JellyfinUserId: {0}", jellyfinUser.Id); continue; } var isConnectUser = jellyfinUser.ConnectUserName.HasValue(); @@ -114,7 +114,6 @@ namespace Ombi.Schedule.Jobs.Jellyfin { UserName = jellyfinUser.Name, ProviderUserId = jellyfinUser.Id, - Alias = isConnectUser ? jellyfinUser.Name : string.Empty, MovieRequestLimit = userManagementSettings.MovieRequestLimit, EpisodeRequestLimit = userManagementSettings.EpisodeRequestLimit }; diff --git a/src/Ombi/ClientApp/src/app/interfaces/IUser.ts b/src/Ombi/ClientApp/src/app/interfaces/IUser.ts index 30f4837d2..b3ce9d165 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/IUser.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/IUser.ts @@ -48,7 +48,7 @@ export enum UserType { LocalUser = 1, PlexUser = 2, EmbyUser = 3, - JellyfinUser = 4, + JellyfinUser = 5, } export interface IIdentityResult { From 30cd96f82a1164e11eb4fe4efbb6456cb536601a Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sat, 12 Dec 2020 03:12:08 -0500 Subject: [PATCH 44/82] Fix bad controller replacement --- src/Ombi/Controllers/V1/JobController.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Ombi/Controllers/V1/JobController.cs b/src/Ombi/Controllers/V1/JobController.cs index 7f05474dc..b8ae1446b 100644 --- a/src/Ombi/Controllers/V1/JobController.cs +++ b/src/Ombi/Controllers/V1/JobController.cs @@ -93,10 +93,21 @@ namespace Ombi.Controllers.V1 } /// - /// Runs the Jellyfin User importer + /// Runs the Emby User importer /// /// [HttpPost("embyuserimporter")] + public async Task EmbyUserImporter() + { + await OmbiQuartz.TriggerJob(nameof(IEmbyUserImporter), "Emby"); + return true; + } + + /// + /// Runs the Jellyfin User importer + /// + /// + [HttpPost("jellyfinuserimporter")] public async Task JellyfinUserImporter() { await OmbiQuartz.TriggerJob(nameof(IJellyfinUserImporter), "Jellyfin"); From f52a74eba6b0073c94be4f8b7b16363baa7f0e73 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sat, 12 Dec 2020 16:04:16 -0500 Subject: [PATCH 45/82] Properly set usertype for Jellyfin users --- src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs index d0d95ef3c..790052309 100644 --- a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs +++ b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs @@ -103,20 +103,21 @@ namespace Ombi.Schedule.Jobs.Jellyfin if (existingJellyfinUser == null) { - if (!jellyfinUser.ConnectUserName.HasValue() && !jellyfinUser.Name.HasValue()) + if (!jellyfinUser.Name.HasValue()) { _log.LogInformation("Could not create Jellyfin user since the have no username, JellyfinUserId: {0}", jellyfinUser.Id); continue; } - var isConnectUser = jellyfinUser.ConnectUserName.HasValue(); // Create this users var newUser = new OmbiUser { UserName = jellyfinUser.Name, + UserType = UserType.JellyfinUser, ProviderUserId = jellyfinUser.Id, MovieRequestLimit = userManagementSettings.MovieRequestLimit, EpisodeRequestLimit = userManagementSettings.EpisodeRequestLimit }; + _log.LogInformation("Creating Jellyfin user {0}", newUser.UserName); var result = await _userManager.CreateAsync(newUser); if (!result.Succeeded) { From 71007ca5b7d1456acc1cfe992811a3b0ec3b77fe Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sat, 12 Dec 2020 16:18:03 -0500 Subject: [PATCH 46/82] Add MySQL migration --- .../20201212014227_Jellyfin.Designer.cs | 506 ++++++++++++++++++ .../ExternalMySql/20201212014227_Jellyfin.cs | 76 +++ .../ExternalMySqlContextModelSnapshot.cs | 113 ++++ 3 files changed, 695 insertions(+) create mode 100644 src/Ombi.Store/Migrations/ExternalMySql/20201212014227_Jellyfin.Designer.cs create mode 100644 src/Ombi.Store/Migrations/ExternalMySql/20201212014227_Jellyfin.cs diff --git a/src/Ombi.Store/Migrations/ExternalMySql/20201212014227_Jellyfin.Designer.cs b/src/Ombi.Store/Migrations/ExternalMySql/20201212014227_Jellyfin.Designer.cs new file mode 100644 index 000000000..19189c2e0 --- /dev/null +++ b/src/Ombi.Store/Migrations/ExternalMySql/20201212014227_Jellyfin.Designer.cs @@ -0,0 +1,506 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context.Sqlite; + +namespace Ombi.Store.Migrations.ExternalSqlite +{ + [DbContext(typeof(ExternalSqliteContext))] + [Migration("20201212014227_Jellyfin")] + partial class Jellyfin + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.1"); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("TheMovieDbId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("EmbyId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("TheMovieDbId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TvDbId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("EmbyId") + .HasColumnType("TEXT"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("TheMovieDbId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TvDbId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("JellyfinId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("TheMovieDbId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TvDbId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("JellyfinContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("JellyfinId") + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("TheMovieDbId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TvDbId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("JellyfinEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("ArtistId") + .HasColumnType("INTEGER"); + + b.Property("ForeignAlbumId") + .HasColumnType("TEXT"); + + b.Property("Monitored") + .HasColumnType("INTEGER"); + + b.Property("PercentOfTracks") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TrackCount") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ArtistId") + .HasColumnType("INTEGER"); + + b.Property("ArtistName") + .HasColumnType("TEXT"); + + b.Property("ForeignArtistId") + .HasColumnType("TEXT"); + + b.Property("Monitored") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("GrandparentKey") + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("INTEGER"); + + b.Property("ParentKey") + .HasColumnType("INTEGER"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ParentKey") + .HasColumnType("INTEGER"); + + b.Property("PlexContentId") + .HasColumnType("INTEGER"); + + b.Property("PlexServerContentId") + .HasColumnType("INTEGER"); + + b.Property("SeasonKey") + .HasColumnType("INTEGER"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("INTEGER"); + + b.Property("Quality") + .HasColumnType("TEXT"); + + b.Property("ReleaseYear") + .HasColumnType("TEXT"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("TheMovieDbId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TvDbId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("HasFile") + .HasColumnType("INTEGER"); + + b.Property("TheMovieDbId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("TvDbId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("TvDbId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("TvDbId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("HasFile") + .HasColumnType("INTEGER"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("TvDbId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + + b.Navigation("Series"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b => + { + b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("JellyfinId"); + + b.Navigation("Series"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Series"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", null) + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Navigation("Episodes"); + + b.Navigation("Seasons"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/ExternalMySql/20201212014227_Jellyfin.cs b/src/Ombi.Store/Migrations/ExternalMySql/20201212014227_Jellyfin.cs new file mode 100644 index 000000000..f1eb6ebcf --- /dev/null +++ b/src/Ombi.Store/Migrations/ExternalMySql/20201212014227_Jellyfin.cs @@ -0,0 +1,76 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations.ExternalSqlite +{ + public partial class Jellyfin : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "JellyfinContent", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Title = table.Column(nullable: true), + ProviderId = table.Column(nullable: true), + JellyfinId = table.Column(nullable: false), + Type = table.Column(nullable: false), + AddedAt = table.Column(nullable: false), + ImdbId = table.Column(nullable: true), + TheMovieDbId = table.Column(nullable: true), + TvDbId = table.Column(nullable: true), + Url = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_JellyfinContent", x => x.Id); + table.UniqueConstraint("AK_JellyfinContent_JellyfinId", x => x.JellyfinId); + }); + + migrationBuilder.CreateTable( + name: "JellyfinEpisode", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Title = table.Column(nullable: true), + JellyfinId = table.Column(nullable: true), + EpisodeNumber = table.Column(nullable: false), + SeasonNumber = table.Column(nullable: false), + ParentId = table.Column(nullable: true), + ProviderId = table.Column(nullable: true), + AddedAt = table.Column(nullable: false), + TvDbId = table.Column(nullable: true), + ImdbId = table.Column(nullable: true), + TheMovieDbId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_JellyfinEpisode", x => x.Id); + table.ForeignKey( + name: "FK_JellyfinEpisode_JellyfinContent_ParentId", + column: x => x.ParentId, + principalTable: "JellyfinContent", + principalColumn: "JellyfinId", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_JellyfinEpisode_ParentId", + table: "JellyfinEpisode", + column: "ParentId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "JellyfinContent"); + + migrationBuilder.DropTable( + name: "JellyfinEpisode"); + } + } +} diff --git a/src/Ombi.Store/Migrations/ExternalMySql/ExternalMySqlContextModelSnapshot.cs b/src/Ombi.Store/Migrations/ExternalMySql/ExternalMySqlContextModelSnapshot.cs index 890e24b76..70312e542 100644 --- a/src/Ombi.Store/Migrations/ExternalMySql/ExternalMySqlContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/ExternalMySql/ExternalMySqlContextModelSnapshot.cs @@ -113,6 +113,88 @@ namespace Ombi.Store.Migrations.ExternalMySql b.ToTable("EmbyEpisode"); }); + modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AddedAt") + .HasColumnType("datetime(6)"); + + b.Property("ImdbId") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("JellyfinId") + .IsRequired() + .HasColumnType("varchar(255) CHARACTER SET utf8mb4"); + + b.Property("ProviderId") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("TheMovieDbId") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("Title") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("TvDbId") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("Url") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.HasKey("Id"); + + b.ToTable("JellyfinContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AddedAt") + .HasColumnType("dateime(6)"); + + b.Property("EpisodeNumber") + .HasColumnType("int"); + + b.Property("ImdbId") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("JellyfinId") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("ParentId") + .HasColumnType("varchar(255) CHARACTER SET utf8mb4"); + + b.Property("ProviderId") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("SeasonNumber") + .HasColumnType("int"); + + b.Property("TheMovieDbId") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("Title") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("TvDbId") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("JellyfinEpisode"); + }); + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => { b.Property("Id") @@ -368,6 +450,18 @@ namespace Ombi.Store.Migrations.ExternalMySql .WithMany("Episodes") .HasForeignKey("ParentId") .HasPrincipalKey("EmbyId"); + + b.Navigation("Series"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b => + { + b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("JellyfinId"); + + b.Navigation("Series"); }); modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => @@ -378,6 +472,8 @@ namespace Ombi.Store.Migrations.ExternalMySql .HasPrincipalKey("Key") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + + b.Navigation("Series"); }); modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => @@ -386,6 +482,23 @@ namespace Ombi.Store.Migrations.ExternalMySql .WithMany("Seasons") .HasForeignKey("PlexServerContentId"); }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b => + { + b.Navigation("Episodes"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Navigation("Episodes"); + + b.Navigation("Seasons"); + }); #pragma warning restore 612, 618 } } From c03eaa12da9a75624c12389173bf89ee8aca2308 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sat, 12 Dec 2020 16:23:52 -0500 Subject: [PATCH 47/82] Remove superfluous newline --- src/Ombi.Api.Jellyfin/Models/Media/JellyfinImagetags.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Ombi.Api.Jellyfin/Models/Media/JellyfinImagetags.cs b/src/Ombi.Api.Jellyfin/Models/Media/JellyfinImagetags.cs index d1a87ddbe..d2dcc0f8a 100644 --- a/src/Ombi.Api.Jellyfin/Models/Media/JellyfinImagetags.cs +++ b/src/Ombi.Api.Jellyfin/Models/Media/JellyfinImagetags.cs @@ -5,7 +5,6 @@ public string Primary { get; set; } public string Logo { get; set; } public string Thumb { get; set; } - public string Banner { get; set; } } -} \ No newline at end of file +} From 555406c1c49fe02bbffd68f098be251c8ba915d7 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sat, 12 Dec 2020 16:43:17 -0500 Subject: [PATCH 48/82] Fix up invalid MySQL migration Rename files, change import and namespace. --- ...in.Designer.cs => 20201212163118_Jellyfin.Designer.cs} | 6 +++--- ...01212014227_Jellyfin.cs => 20201212163118_Jellyfin.cs} | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) rename src/Ombi.Store/Migrations/ExternalMySql/{20201212014227_Jellyfin.Designer.cs => 20201212163118_Jellyfin.Designer.cs} (99%) rename src/Ombi.Store/Migrations/ExternalMySql/{20201212014227_Jellyfin.cs => 20201212163118_Jellyfin.cs} (95%) diff --git a/src/Ombi.Store/Migrations/ExternalMySql/20201212014227_Jellyfin.Designer.cs b/src/Ombi.Store/Migrations/ExternalMySql/20201212163118_Jellyfin.Designer.cs similarity index 99% rename from src/Ombi.Store/Migrations/ExternalMySql/20201212014227_Jellyfin.Designer.cs rename to src/Ombi.Store/Migrations/ExternalMySql/20201212163118_Jellyfin.Designer.cs index 19189c2e0..93c128c5c 100644 --- a/src/Ombi.Store/Migrations/ExternalMySql/20201212014227_Jellyfin.Designer.cs +++ b/src/Ombi.Store/Migrations/ExternalMySql/20201212163118_Jellyfin.Designer.cs @@ -4,11 +4,11 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Ombi.Store.Context.Sqlite; +using Ombi.Store.Context.MySql; -namespace Ombi.Store.Migrations.ExternalSqlite +namespace Ombi.Store.Migrations.ExternalMySql { - [DbContext(typeof(ExternalSqliteContext))] + [DbContext(typeof(ExternalMySqlContext))] [Migration("20201212014227_Jellyfin")] partial class Jellyfin { diff --git a/src/Ombi.Store/Migrations/ExternalMySql/20201212014227_Jellyfin.cs b/src/Ombi.Store/Migrations/ExternalMySql/20201212163118_Jellyfin.cs similarity index 95% rename from src/Ombi.Store/Migrations/ExternalMySql/20201212014227_Jellyfin.cs rename to src/Ombi.Store/Migrations/ExternalMySql/20201212163118_Jellyfin.cs index f1eb6ebcf..bb23c95c9 100644 --- a/src/Ombi.Store/Migrations/ExternalMySql/20201212014227_Jellyfin.cs +++ b/src/Ombi.Store/Migrations/ExternalMySql/20201212163118_Jellyfin.cs @@ -1,8 +1,8 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using System; -using System.Collections.Generic; +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; -namespace Ombi.Store.Migrations.ExternalSqlite +namespace Ombi.Store.Migrations.ExternalMySql { public partial class Jellyfin : Migration { From 6b24ab7f834b688d5662e496fe803ea0cdea8855 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sat, 12 Dec 2020 18:16:24 -0500 Subject: [PATCH 49/82] Correct tests for Jellyfin types --- .../Authentication/OmbiUserManagerTests.cs | 2 +- src/Ombi.Helpers.Tests/EmbyHelperTests.cs | 18 ------------ src/Ombi.Helpers.Tests/JellyfinHelperTests.cs | 29 +++++++++++++++++++ 3 files changed, 30 insertions(+), 19 deletions(-) create mode 100644 src/Ombi.Helpers.Tests/JellyfinHelperTests.cs diff --git a/src/Ombi.Core.Tests/Authentication/OmbiUserManagerTests.cs b/src/Ombi.Core.Tests/Authentication/OmbiUserManagerTests.cs index bd6e8d5a2..f4e6f59a3 100644 --- a/src/Ombi.Core.Tests/Authentication/OmbiUserManagerTests.cs +++ b/src/Ombi.Core.Tests/Authentication/OmbiUserManagerTests.cs @@ -30,7 +30,7 @@ namespace Ombi.Core.Tests.Authentication AuthenticationSettings.Setup(x => x.GetSettingsAsync()) .ReturnsAsync(new AuthenticationSettings()); _um = new OmbiUserManager(UserStore.Object, null, null, null, null, null, null, null, null, - PlexApi.Object, null, null, AuthenticationSettings.Object); + PlexApi.Object, null, null, null, null, AuthenticationSettings.Object); } public OmbiUserManager _um { get; set; } diff --git a/src/Ombi.Helpers.Tests/EmbyHelperTests.cs b/src/Ombi.Helpers.Tests/EmbyHelperTests.cs index 261ba87cc..50ac9c48f 100644 --- a/src/Ombi.Helpers.Tests/EmbyHelperTests.cs +++ b/src/Ombi.Helpers.Tests/EmbyHelperTests.cs @@ -15,13 +15,6 @@ namespace Ombi.Helpers.Tests return EmbyHelper.GetEmbyMediaUrl(mediaId, serverId, url); } - [TestCaseSource(nameof(JellyfinUrlData))] - public string TestJellyfinUrl(string mediaId, string url, string serverId) - { - // http://192.168.68.X:8097/web/index.html#!/details?id=7ffe222498445d5ebfddb31bc4fa9a6d&serverId=50cce67f0baa425093d189b3017331fb - return EmbyHelper.GetEmbyMediaUrl(mediaId, serverId, url, true); - } - public static IEnumerable UrlData { get @@ -33,16 +26,5 @@ namespace Ombi.Helpers.Tests yield return new TestCaseData(mediaId.ToString(), string.Empty, "1").Returns($"https://app.emby.media/web/index.html#!/item?id={mediaId}&serverId=1").SetName("EmbyHelper_GetMediaUrl_WithOutCustomDomain"); } } - - public static IEnumerable JellyfinUrlData - { - get - { - var mediaId = 1; - yield return new TestCaseData(mediaId.ToString(), "http://google.com", "1").Returns($"http://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("EmbyHelperJellyfin_GetMediaUrl_WithCustomDomain_WithoutTrailingSlash"); - yield return new TestCaseData(mediaId.ToString(), "http://google.com/", "1").Returns($"http://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("EmbyHelperJellyfin_GetMediaUrl_WithCustomDomain"); - yield return new TestCaseData(mediaId.ToString(), "https://google.com/", "1").Returns($"https://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("EmbyHelperJellyfin_GetMediaUrl_WithCustomDomain_Https"); - } - } } } diff --git a/src/Ombi.Helpers.Tests/JellyfinHelperTests.cs b/src/Ombi.Helpers.Tests/JellyfinHelperTests.cs new file mode 100644 index 000000000..e2c235443 --- /dev/null +++ b/src/Ombi.Helpers.Tests/JellyfinHelperTests.cs @@ -0,0 +1,29 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Ombi.Helpers.Tests +{ + [TestFixture] + public class EmbyHelperTests + { + [TestCaseSource(nameof(UrlData))] + public string TestUrl(string mediaId, string url, string serverId) + { + // http://192.168.68.X:8097/web/index.html#!/details?id=7ffe222498445d5ebfddb31bc4fa9a6d&serverId=50cce67f0baa425093d189b3017331fb + return JellyfinHelper.GetJellyfinMediaUrl(mediaId, serverId, url); + } + + public static IEnumerable UrlData + { + get + { + var mediaId = 1; + yield return new TestCaseData(mediaId.ToString(), "http://google.com", "1").Returns($"http://google.com/web/index.html#!/item?id={mediaId}&serverId=1").SetName("JellyfinHelper_GetMediaUrl_WithCustomDomain_WithoutTrailingSlash"); + yield return new TestCaseData(mediaId.ToString(), "http://google.com/", "1").Returns($"http://google.com/web/index.html#!/item?id={mediaId}&serverId=1").SetName("JellyfinHelper_GetMediaUrl_WithCustomDomain"); + yield return new TestCaseData(mediaId.ToString(), "https://google.com/", "1").Returns($"https://google.com/web/index.html#!/item?id={mediaId}&serverId=1").SetName("JellyfinHelper_GetMediaUrl_WithCustomDomain_Https"); + } + } + } +} From bb60365109406ea2a4afd5532635c239c460fcb4 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sat, 12 Dec 2020 18:28:40 -0500 Subject: [PATCH 50/82] Fix remaining tests --- .../Rule/Search/EmbyAvailabilityRuleTests.cs | 103 --------------- .../Search/JellyfinAvailabilityRuleTests.cs | 117 ++++++++++++++++++ src/Ombi.Helpers.Tests/JellyfinHelperTests.cs | 8 +- src/Ombi.Test.Common/MockHelper.cs | 2 +- 4 files changed, 122 insertions(+), 108 deletions(-) create mode 100644 src/Ombi.Core.Tests/Rule/Search/JellyfinAvailabilityRuleTests.cs diff --git a/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs index ed3e60631..132f51e49 100644 --- a/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs +++ b/src/Ombi.Core.Tests/Rule/Search/EmbyAvailabilityRuleTests.cs @@ -115,107 +115,4 @@ namespace Ombi.Core.Tests.Rule.Search Assert.False(search.Available); } } - - public class JellyfinAvailabilityRuleTests - { - [SetUp] - public void Setup() - { - ContextMock = new Mock(); - SettingsMock = new Mock>(); - Rule = new JellyfinAvailabilityRule(ContextMock.Object, SettingsMock.Object); - } - - private JellyfinAvailabilityRule Rule { get; set; } - private Mock ContextMock { get; set; } - private Mock> SettingsMock { get; set; } - - [Test] - public async Task Movie_ShouldBe_Available_WhenFoundInJellyfin() - { - SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new JellyfinSettings()); - ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny())).ReturnsAsync(new JellyfinContent - { - ProviderId = "123" - }); - var search = new SearchMovieViewModel() - { - TheMovieDbId = "123", - }; - var result = await Rule.Execute(search); - - Assert.True(result.Success); - Assert.True(search.Available); - } - - [Test] - public async Task Movie_Has_Custom_Url_When_Specified_In_Settings() - { - SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new JellyfinSettings - { - Enable = true, - Servers = new List - { - new JellyfinServers - { - ServerHostname = "http://test.com/", - ServerId = "8" - } - } - }); - ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny())).ReturnsAsync(new JellyfinContent - { - ProviderId = "123", - JellyfinId = 1.ToString(), - }); - var search = new SearchMovieViewModel() - { - TheMovieDbId = "123", - }; - var result = await Rule.Execute(search); - - Assert.True(result.Success); - Assert.That(search.JellyfinUrl, Is.EqualTo("http://test.com/web/index.html#!/item?id=1&serverId=8")); - } - - [Test] - public async Task Movie_Uses_Default_Url_When() - { - SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new JellyfinSettings - { - Enable = true, - Servers = new List - { - new JellyfinServers - { - ServerHostname = string.Empty, - ServerId = "8" - } - } - }); - ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny())).ReturnsAsync(new JellyfinContent - { - ProviderId = "123", - JellyfinId = 1.ToString() - }); - var search = new SearchMovieViewModel() - { - TheMovieDbId = "123", - }; - var result = await Rule.Execute(search); - - Assert.True(result.Success); - } - - [Test] - public async Task Movie_ShouldBe_NotAvailable_WhenNotFoundInJellyfin() - { - ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny())).Returns(Task.FromResult(default(JellyfinContent))); - var search = new SearchMovieViewModel(); - var result = await Rule.Execute(search); - - Assert.True(result.Success); - Assert.False(search.Available); - } - } } diff --git a/src/Ombi.Core.Tests/Rule/Search/JellyfinAvailabilityRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/JellyfinAvailabilityRuleTests.cs new file mode 100644 index 000000000..5da69a181 --- /dev/null +++ b/src/Ombi.Core.Tests/Rule/Search/JellyfinAvailabilityRuleTests.cs @@ -0,0 +1,117 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Moq; +using NUnit.Framework; +using Ombi.Core.Models.Search; +using Ombi.Core.Rule.Rules.Search; +using Ombi.Core.Settings; +using Ombi.Core.Settings.Models.External; +using Ombi.Store.Entities; +using Ombi.Store.Repository; +using Ombi.Store.Repository.Requests; + +namespace Ombi.Core.Tests.Rule.Search +{ + public class JellyfinAvailabilityRuleTests + { + [SetUp] + public void Setup() + { + ContextMock = new Mock(); + SettingsMock = new Mock>(); + Rule = new JellyfinAvailabilityRule(ContextMock.Object, SettingsMock.Object); + } + + private JellyfinAvailabilityRule Rule { get; set; } + private Mock ContextMock { get; set; } + private Mock> SettingsMock { get; set; } + + [Test] + public async Task Movie_ShouldBe_Available_WhenFoundInJellyfin() + { + SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new JellyfinSettings()); + ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny())).ReturnsAsync(new JellyfinContent + { + ProviderId = "123" + }); + var search = new SearchMovieViewModel() + { + TheMovieDbId = "123", + }; + var result = await Rule.Execute(search); + + Assert.True(result.Success); + Assert.True(search.Available); + } + + [Test] + public async Task Movie_Has_Custom_Url_When_Specified_In_Settings() + { + SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new JellyfinSettings + { + Enable = true, + Servers = new List + { + new JellyfinServers + { + ServerHostname = "http://test.com/", + ServerId = "8" + } + } + }); + ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny())).ReturnsAsync(new JellyfinContent + { + ProviderId = "123", + JellyfinId = 1.ToString(), + }); + var search = new SearchMovieViewModel() + { + TheMovieDbId = "123", + }; + var result = await Rule.Execute(search); + + Assert.True(result.Success); + Assert.That(search.JellyfinUrl, Is.EqualTo("http://test.com/web/index.html#!/details?id=1&serverId=8")); + } + + [Test] + public async Task Movie_Uses_Default_Url_When() + { + SettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new JellyfinSettings + { + Enable = true, + Servers = new List + { + new JellyfinServers + { + ServerHostname = string.Empty, + ServerId = "8" + } + } + }); + ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny())).ReturnsAsync(new JellyfinContent + { + ProviderId = "123", + JellyfinId = 1.ToString() + }); + var search = new SearchMovieViewModel() + { + TheMovieDbId = "123", + }; + var result = await Rule.Execute(search); + + Assert.True(result.Success); + } + + [Test] + public async Task Movie_ShouldBe_NotAvailable_WhenNotFoundInJellyfin() + { + ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny())).Returns(Task.FromResult(default(JellyfinContent))); + var search = new SearchMovieViewModel(); + var result = await Rule.Execute(search); + + Assert.True(result.Success); + Assert.False(search.Available); + } + } +} diff --git a/src/Ombi.Helpers.Tests/JellyfinHelperTests.cs b/src/Ombi.Helpers.Tests/JellyfinHelperTests.cs index e2c235443..df3f960d4 100644 --- a/src/Ombi.Helpers.Tests/JellyfinHelperTests.cs +++ b/src/Ombi.Helpers.Tests/JellyfinHelperTests.cs @@ -6,7 +6,7 @@ using System.Text; namespace Ombi.Helpers.Tests { [TestFixture] - public class EmbyHelperTests + public class JellyfinHelperTests { [TestCaseSource(nameof(UrlData))] public string TestUrl(string mediaId, string url, string serverId) @@ -20,9 +20,9 @@ namespace Ombi.Helpers.Tests get { var mediaId = 1; - yield return new TestCaseData(mediaId.ToString(), "http://google.com", "1").Returns($"http://google.com/web/index.html#!/item?id={mediaId}&serverId=1").SetName("JellyfinHelper_GetMediaUrl_WithCustomDomain_WithoutTrailingSlash"); - yield return new TestCaseData(mediaId.ToString(), "http://google.com/", "1").Returns($"http://google.com/web/index.html#!/item?id={mediaId}&serverId=1").SetName("JellyfinHelper_GetMediaUrl_WithCustomDomain"); - yield return new TestCaseData(mediaId.ToString(), "https://google.com/", "1").Returns($"https://google.com/web/index.html#!/item?id={mediaId}&serverId=1").SetName("JellyfinHelper_GetMediaUrl_WithCustomDomain_Https"); + yield return new TestCaseData(mediaId.ToString(), "http://google.com", "1").Returns($"http://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("JellyfinHelper_GetMediaUrl_WithCustomDomain_WithoutTrailingSlash"); + yield return new TestCaseData(mediaId.ToString(), "http://google.com/", "1").Returns($"http://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("JellyfinHelper_GetMediaUrl_WithCustomDomain"); + yield return new TestCaseData(mediaId.ToString(), "https://google.com/", "1").Returns($"https://google.com/web/index.html#!/details?id={mediaId}&serverId=1").SetName("JellyfinHelper_GetMediaUrl_WithCustomDomain_Https"); } } } diff --git a/src/Ombi.Test.Common/MockHelper.cs b/src/Ombi.Test.Common/MockHelper.cs index 00e327abc..8d3f5ce8d 100644 --- a/src/Ombi.Test.Common/MockHelper.cs +++ b/src/Ombi.Test.Common/MockHelper.cs @@ -14,7 +14,7 @@ namespace Ombi.Test.Common { var store = new Mock>(); //var u = new OmbiUserManager(store.Object, null, null, null, null, null, null, null, null,null,null,null,null) - var mgr = new Mock(store.Object, null, null, null, null, null, null, null, null, null, null, null, null); + var mgr = new Mock(store.Object, null, null, null, null, null, null, null, null, null, null, null, null, null, null); mgr.Object.UserValidators.Add(new UserValidator()); mgr.Object.PasswordValidators.Add(new PasswordValidator()); From cb234e17761e979a17a49fe6c01bf27dce6efd73 Mon Sep 17 00:00:00 2001 From: goldenpipes Date: Sun, 13 Dec 2020 10:44:10 -0600 Subject: [PATCH 51/82] jobs page and the update page --- .../src/app/settings/jobs/jobs.component.html | 194 ++++++++++-------- .../src/app/settings/jobs/jobs.component.scss | 13 +- .../app/settings/update/update.component.html | 104 +++++----- .../app/settings/update/update.component.scss | 11 +- 4 files changed, 176 insertions(+), 146 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.html b/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.html index ec2a42c99..88c4bdfbb 100644 --- a/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.html +++ b/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.html @@ -1,123 +1,137 @@ - - - + -
+
Job Settings - -
- Changes to any of the below requires you to restart Ombi. - You can generate valid CRON Expressions here: https://www.cronmaker.com/ -
- - - The Sonarr Sync is required - + + Changes require a restart.

+ You can generate valid CRON Expressions here: https://www.cronmaker.com/ +

+
+ + Sonarr Sync + + The Sonarr Sync is required + +
-
- - - The SickRage Sync is required - +
+ + SickRage Sync + + The SickRage Sync is required +
-
- - - The Radarr Sync is required - +
+ + Radarr Sync + + The Radarr Sync is required +
-
- - - The Lidarr Sync is required - +
+ + Lidarr Sync + + The Lidarr Sync is required +
-
- - - The CouchPotato Sync is required - +
+ + CouchPotato Sync + + The CouchPotato Sync is required +
-
- - - The Automatic Update is required - +
+ + Automatic Update + + The Automatic Update is required +
-
- - - The Retry Requests is required - -
-
-
-
- - - The Plex Sync is required - -
-
- - - The Plex Sync is required - +
+ + Retry Failed Requests + + The Retry Requests is required +
-
- - - The Emby Sync is required - +
+ + Plex Sync + + The Plex Sync is required + +
+
+ + Plex Recently Added Sync + + The Plex Sync is required +
-
- - - The User Importer is required - +
+ + Emby Sync + + The Emby Sync is required +
-
- - - The Newsletter is required - +
+ + User Importer + + The User Importer is required + +
+ +
+ + Newsletter + + The Newsletter is required +
-
- - - The Issues Purge is required - +
+ + Issue Purge/Delete + + The Issues Purge is required +
-
- - - The Media Database Refresh is required - +
+ + Media Data Refresh + + The Media Database Refresh is required +
-
- - - Auto Available Request Deletion is required - +
+ + Auto Available Request Deletion + + Auto Available Request Deletion is required +
-
+
- + +
-
+
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.scss b/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.scss index 018bebef8..ff6ee2525 100644 --- a/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.scss +++ b/src/Ombi/ClientApp/src/app/settings/jobs/jobs.component.scss @@ -1,5 +1,16 @@ .small-middle-container{ margin: auto; - width: 85%; + width: 95%; margin-top:10px; +} +.cronBox { + width: 300px; + display: inline-flex; + padding-left: 0px; + margin-right: 1em; +} +.cronbtn { + max-height: 36px; + margin-top: 1em; + margin-left: .5em; } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/settings/update/update.component.html b/src/Ombi/ClientApp/src/app/settings/update/update.component.html index 990015d76..6d0688697 100644 --- a/src/Ombi/ClientApp/src/app/settings/update/update.component.html +++ b/src/Ombi/ClientApp/src/app/settings/update/update.component.html @@ -4,85 +4,87 @@
Update Settings -
-
- -
-
- -
+
+
+
-
-
-
-
- - -
+
+ +
+ + Enable Automatic Update +
- -
-
- - -
+
+ + Running as a Windows Service +
-
-
- - -
+
+ + Use your own updater script +
- +
-
- - +
+ Windows Service Name + +
For information how to use this, please press the wiki button at the top of the page -
- - +
+ + Script Path + +
By default the process name is Ombi, but this could be different for your system. We need to know the process name so we can kill that process to update the files. -
- - +
+ + Ombi Process Name + +
-
-
- -
-
-
-
+ + +
If you are getting any permissions issues, you can specify a user for the update process to run under. -
- - +
+ + Username + +
-
- - +
+ + Password + + +
+
+
+
+
diff --git a/src/Ombi/ClientApp/src/app/settings/update/update.component.scss b/src/Ombi/ClientApp/src/app/settings/update/update.component.scss index 60e52d81a..044e7eb2d 100644 --- a/src/Ombi/ClientApp/src/app/settings/update/update.component.scss +++ b/src/Ombi/ClientApp/src/app/settings/update/update.component.scss @@ -1,5 +1,8 @@ -.small-middle-container{ - margin: auto; - width: 85%; - margin-top:10px; +.small-middle-container { + margin: auto; + width: 95%; + margin-top: 10px; } +.textEntry { + width: 300px; +} \ No newline at end of file From 093fb673ed5dbfa24de69df7aa2eb7f522ac455a Mon Sep 17 00:00:00 2001 From: goldenpipes Date: Mon, 14 Dec 2020 18:56:10 -0600 Subject: [PATCH 52/82] added a new class for the login card to allow changing the card colour, etc --- src/Ombi/ClientApp/src/app/login/login.component.html | 2 +- src/Ombi/ClientApp/src/app/login/login.component.scss | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Ombi/ClientApp/src/app/login/login.component.html b/src/Ombi/ClientApp/src/app/login/login.component.html index 9fcc4c1ee..27b8d827a 100644 --- a/src/Ombi/ClientApp/src/app/login/login.component.html +++ b/src/Ombi/ClientApp/src/app/login/login.component.html @@ -3,7 +3,7 @@
- +
- Available - Processing - Request + {{'Common.Available' | translate}} + {{'Common.ProcessingRequest' | translate}} Selected Pending Approval + id="pendingApprovalLabel">{{'Common.PendingApproval' | translate}} Not Requested From e6751903a76df66d83d46ce93ed9f3172d8cc459 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Thu, 17 Dec 2020 21:42:58 +0000 Subject: [PATCH 55/82] Fixed an issue where we could grab the wrong imdbid --- .../Jobs/Ombi/RefreshMetadata.cs | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs index 30878adaf..f7d9ae1d2 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs @@ -128,7 +128,7 @@ namespace Ombi.Schedule.Jobs.Ombi if (!hasImdb) { - var id = await GetImdbId(hasTheMovieDb, hasTvDbId, show.Title, show.TheMovieDbId, show.TvDbId); + var id = await GetImdbId(hasTheMovieDb, hasTvDbId, show.Title, show.TheMovieDbId, show.TvDbId, RequestType.TvShow); show.ImdbId = id; _plexRepo.UpdateWithoutSave(show); } @@ -162,7 +162,7 @@ namespace Ombi.Schedule.Jobs.Ombi if (!hasImdb) { - var id = await GetImdbId(hasTheMovieDb, hasTvDbId, show.Title, show.TheMovieDbId, show.TvDbId); + var id = await GetImdbId(hasTheMovieDb, hasTvDbId, show.Title, show.TheMovieDbId, show.TvDbId, RequestType.TvShow); show.ImdbId = id; _embyRepo.UpdateWithoutSave(show); } @@ -193,7 +193,7 @@ namespace Ombi.Schedule.Jobs.Ombi if (!hasImdb) { - var imdbId = await GetImdbId(hasTheMovieDb, false, movie.Title, movie.TheMovieDbId, string.Empty); + var imdbId = await GetImdbId(hasTheMovieDb, false, movie.Title, movie.TheMovieDbId, string.Empty, RequestType.Movie); movie.ImdbId = imdbId; _plexRepo.UpdateWithoutSave(movie); } @@ -247,7 +247,7 @@ namespace Ombi.Schedule.Jobs.Ombi if (!movie.HasImdb) { - var imdbId = await GetImdbId(movie.HasTheMovieDb, false, movie.Title, movie.TheMovieDbId, string.Empty); + var imdbId = await GetImdbId(movie.HasTheMovieDb, false, movie.Title, movie.TheMovieDbId, string.Empty, RequestType.Movie); movie.ImdbId = imdbId; _embyRepo.UpdateWithoutSave(movie); } @@ -305,7 +305,7 @@ namespace Ombi.Schedule.Jobs.Ombi return string.Empty; } - private async Task GetImdbId(bool hasTheMovieDb, bool hasTvDbId, string title, string theMovieDbId, string tvDbId) + private async Task GetImdbId(bool hasTheMovieDb, bool hasTvDbId, string title, string theMovieDbId, string tvDbId, RequestType type) { _log.LogInformation("The media item {0} does not have a ImdbId, searching for ImdbId", title); // Looks like TV Maze does not provide the moviedb id, neither does the TV endpoint on TheMovieDb @@ -314,13 +314,22 @@ namespace Ombi.Schedule.Jobs.Ombi _log.LogInformation("The show {0} has TheMovieDbId but not ImdbId, searching for ImdbId", title); if (int.TryParse(theMovieDbId, out var id)) { - var result = await _movieApi.GetTvExternals(id); - - return result.imdb_id; + switch (type) + { + case RequestType.TvShow: + var result = await _movieApi.GetTvExternals(id); + return result.imdb_id; + case RequestType.Movie: + var r = await _movieApi.GetMovieInformationWithExtraInfo(id); + return r.ImdbId; + default: + break; + } + } } - if (hasTvDbId) + if (hasTvDbId && type == RequestType.TvShow) { _log.LogInformation("The show {0} has tvdbid but not ImdbId, searching for ImdbId", title); if (int.TryParse(tvDbId, out var id)) From f57c147a47e6c49e134f0690e4621699126d2fa8 Mon Sep 17 00:00:00 2001 From: safran_ Date: Thu, 31 Dec 2020 21:27:23 +0100 Subject: [PATCH 56/82] =?UTF-8?q?=E2=9C=A8=20CSS=20-=20Fix=20jagged=20edge?= =?UTF-8?q?s=20on=20diagonal=20gradient=20overlay=20on=20landing=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Ombi/ClientApp/src/app/landingpage/landingpage.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi/ClientApp/src/app/landingpage/landingpage.component.ts b/src/Ombi/ClientApp/src/app/landingpage/landingpage.component.ts index 80bba97b2..4d1d4166c 100644 --- a/src/Ombi/ClientApp/src/app/landingpage/landingpage.component.ts +++ b/src/Ombi/ClientApp/src/app/landingpage/landingpage.component.ts @@ -35,7 +35,7 @@ export class LandingPageComponent implements OnDestroy, OnInit { this.settingsService.getCustomization().subscribe(x => this.customizationSettings = x); this.settingsService.getLandingPage().subscribe(x => this.landingPageSettings = x); this.images.getRandomBackground().subscribe(x => { - this.background = this.sanitizer.bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%), url(" + x.url + ")"); + this.background = this.sanitizer.bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 19%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 79%, transparent 80%), url(" + x.url + ")"); }); this.timer = setInterval(() => { this.cycleBackground(); From 87233a7fd37ee9ca8e4a98518545ca06962e384e Mon Sep 17 00:00:00 2001 From: tidusjar Date: Sat, 2 Jan 2021 00:35:24 +0000 Subject: [PATCH 57/82] Kick out the user when the user has been invalidated. This should solve a few issues with "still being logged in" --- src/Ombi/ClientApp/src/app/app.module.ts | 8 ++++- .../src/app/auth/unauthorized.interceptor.ts | 29 +++++++++++++++++++ .../app/shared/functions/common-functions.ts | 28 ------------------ src/Ombi/Extensions/StartupExtensions.cs | 14 +++++++-- 4 files changed, 48 insertions(+), 31 deletions(-) create mode 100644 src/Ombi/ClientApp/src/app/auth/unauthorized.interceptor.ts delete mode 100644 src/Ombi/ClientApp/src/app/shared/functions/common-functions.ts diff --git a/src/Ombi/ClientApp/src/app/app.module.ts b/src/Ombi/ClientApp/src/app/app.module.ts index 09e5f2955..85d6f7a70 100644 --- a/src/Ombi/ClientApp/src/app/app.module.ts +++ b/src/Ombi/ClientApp/src/app/app.module.ts @@ -1,5 +1,5 @@ import { CommonModule, PlatformLocation, APP_BASE_HREF } from "@angular/common"; -import { HttpClient, HttpClientModule } from "@angular/common/http"; +import { HttpClient, HttpClientModule, HTTP_INTERCEPTORS } from "@angular/common/http"; import { NgModule } from "@angular/core"; import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { BrowserModule } from "@angular/platform-browser"; @@ -66,6 +66,7 @@ import { StorageService } from "./shared/storage/storage-service"; import { SignalRNotificationService } from "./services/signlarnotification.service"; import { MatMenuModule } from "@angular/material/menu"; import { RemainingRequestsComponent } from "./shared/remaining-requests/remaining-requests.component"; +import { UnauthorizedInterceptor } from "./auth/unauthorized.interceptor"; const routes: Routes = [ { path: "*", component: PageNotFoundComponent }, @@ -196,6 +197,11 @@ export function JwtTokenGetter() { { provide: APP_BASE_HREF, useValue: window["baseHref"] + }, + { + provide: HTTP_INTERCEPTORS, + useClass: UnauthorizedInterceptor, + multi: true } ], bootstrap: [AppComponent], diff --git a/src/Ombi/ClientApp/src/app/auth/unauthorized.interceptor.ts b/src/Ombi/ClientApp/src/app/auth/unauthorized.interceptor.ts new file mode 100644 index 000000000..c70b649be --- /dev/null +++ b/src/Ombi/ClientApp/src/app/auth/unauthorized.interceptor.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@angular/core'; +import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http'; +import { Observable, Subject, throwError } from 'rxjs'; +import { catchError, throttleTime } from 'rxjs/operators'; +import { AuthService } from './auth.service'; +import { Router } from '@angular/router'; + +@Injectable() +export class UnauthorizedInterceptor implements HttpInterceptor { + + private throttleLogout = new Subject(); + constructor(private authService: AuthService, private router: Router) { + this.throttleLogout.pipe(throttleTime(5000)).subscribe(url => { + this.authService.logout(); + this.router.navigate(["login"]); + }); + } + + public intercept(request: HttpRequest, next: HttpHandler): Observable> { + return next.handle(request).pipe( + catchError((response: HttpErrorResponse) => { + if (response.status === 401) { + this.throttleLogout.next(); + } + return throwError(response); + } + )); + } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/shared/functions/common-functions.ts b/src/Ombi/ClientApp/src/app/shared/functions/common-functions.ts deleted file mode 100644 index 8d94d8dc0..000000000 --- a/src/Ombi/ClientApp/src/app/shared/functions/common-functions.ts +++ /dev/null @@ -1,28 +0,0 @@ -export function getBaseLocation() { - debugger; - let paths: string[] = location.pathname.split('/').splice(1, 1); - let basePath: string = (paths && paths[0] ? paths[0] : ""); - if(invalidProxies.indexOf(basePath.toUpperCase()) === -1){ - return '/' + basePath; - } - return '/'; -} - -const invalidProxies: string[] = [ - 'DISCOVER', - 'REQUESTS-LIST', - 'SETTINGS', - 'ISSUES', - 'USERMANAGEMENT', - 'RECENTLYADDED', - 'DETAILS', - 'VOTE', - 'LOGIN', - 'LANDINGPAGE', - 'TOKEN', - 'RESET', - 'CUSTOM', - 'AUTH', - 'WIZARD', - "CALENDAR" -] \ No newline at end of file diff --git a/src/Ombi/Extensions/StartupExtensions.cs b/src/Ombi/Extensions/StartupExtensions.cs index e42dfb609..c36c32a40 100644 --- a/src/Ombi/Extensions/StartupExtensions.cs +++ b/src/Ombi/Extensions/StartupExtensions.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; @@ -9,6 +10,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; using Ombi.Config; +using Ombi.Core.Authentication; using Ombi.Helpers; using Ombi.Models.Identity; @@ -102,7 +104,6 @@ namespace Ombi OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"]; - // If the request is for our hub... var path = context.HttpContext.Request.Path; if (!string.IsNullOrEmpty(accessToken) && @@ -111,8 +112,17 @@ namespace Ombi // Read the token out of the query string context.Token = accessToken; } - return Task.CompletedTask; + }, + OnTokenValidated = async context => + { + var userid = context.Principal?.Claims?.Where(x => x.Type.Equals("id", StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault()?.Value ?? default; + var um = context.HttpContext.RequestServices.GetRequiredService(); + var user = await um.FindByIdAsync(userid); + if (user == null) + { + context.Fail("invaild token"); + } } }; }); From 6a062d5c40b017b754cf8a0aec3bcc4a198f532f Mon Sep 17 00:00:00 2001 From: tidusjar Date: Sun, 3 Jan 2021 21:22:52 +0000 Subject: [PATCH 58/82] Fixed a bunch of jellyfin issues --- .../Engine/V2/MovieSearchEngineV2.cs | 1 + .../Rules/Search/JellyfinAvailabilityRule.cs | 3 +- src/Ombi.Helpers/StartupSingleton.cs | 3 + .../Jobs/Ombi/RefreshMetadata.cs | 4 +- ...cs => 20210103205509_Jellyfin.Designer.cs} | 201 +++++++++--------- ...Jellyfin.cs => 20210103205509_Jellyfin.cs} | 46 ++-- .../ExternalMySqlContextModelSnapshot.cs | 86 ++++---- src/Ombi.sln | 6 +- .../episode-request.component.html | 4 +- .../V1/External/TesterController.cs | 29 ++- src/Ombi/Ombi.csproj | 4 + src/Ombi/wwwroot/translations/en.json | 1 + 12 files changed, 214 insertions(+), 174 deletions(-) rename src/Ombi.Store/Migrations/ExternalMySql/{20201212163118_Jellyfin.Designer.cs => 20210103205509_Jellyfin.Designer.cs} (69%) rename src/Ombi.Store/Migrations/ExternalMySql/{20201212163118_Jellyfin.cs => 20210103205509_Jellyfin.cs} (52%) diff --git a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs index b0a78fbb3..1e2110b38 100644 --- a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs @@ -287,6 +287,7 @@ namespace Ombi.Core.Engine.V2 mapped.Requested = viewMovie.Requested; mapped.PlexUrl = viewMovie.PlexUrl; mapped.EmbyUrl = viewMovie.EmbyUrl; + mapped.JellyfinUrl = viewMovie.JellyfinUrl; mapped.Subscribed = viewMovie.Subscribed; mapped.ShowSubscribe = viewMovie.ShowSubscribe; diff --git a/src/Ombi.Core/Rule/Rules/Search/JellyfinAvailabilityRule.cs b/src/Ombi.Core/Rule/Rules/Search/JellyfinAvailabilityRule.cs index 2f6a39f91..0447458d9 100644 --- a/src/Ombi.Core/Rule/Rules/Search/JellyfinAvailabilityRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/JellyfinAvailabilityRule.cs @@ -74,7 +74,8 @@ namespace Ombi.Core.Rule.Rules.Search } else { - obj.JellyfinUrl = JellyfinHelper.GetJellyfinMediaUrl(item.JellyfinId, server?.ServerId, null); + var firstServer = s.Servers?.FirstOrDefault(); + obj.JellyfinUrl = JellyfinHelper.GetJellyfinMediaUrl(item.JellyfinId, firstServer.ServerId, firstServer.FullUri); } } diff --git a/src/Ombi.Helpers/StartupSingleton.cs b/src/Ombi.Helpers/StartupSingleton.cs index 41fd1a9ab..c9df7665a 100644 --- a/src/Ombi.Helpers/StartupSingleton.cs +++ b/src/Ombi.Helpers/StartupSingleton.cs @@ -11,5 +11,8 @@ public string StoragePath { get; set; } public string SecurityKey { get; set; } +#if DEBUG + = "test"; +#endif } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs index 599cea66a..65c85b586 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs @@ -224,7 +224,7 @@ namespace Ombi.Schedule.Jobs.Ombi if (!hasImdb) { - var id = await GetImdbId(hasTheMovieDb, hasTvDbId, show.Title, show.TheMovieDbId, show.TvDbId); + var id = await GetImdbId(hasTheMovieDb, hasTvDbId, show.Title, show.TheMovieDbId, show.TvDbId, RequestType.TvShow); show.ImdbId = id; _jellyfinRepo.UpdateWithoutSave(show); } @@ -364,7 +364,7 @@ namespace Ombi.Schedule.Jobs.Ombi if (!movie.HasImdb) { - var imdbId = await GetImdbId(movie.HasTheMovieDb, false, movie.Title, movie.TheMovieDbId, string.Empty); + var imdbId = await GetImdbId(movie.HasTheMovieDb, false, movie.Title, movie.TheMovieDbId, string.Empty, RequestType.Movie); movie.ImdbId = imdbId; _jellyfinRepo.UpdateWithoutSave(movie); } diff --git a/src/Ombi.Store/Migrations/ExternalMySql/20201212163118_Jellyfin.Designer.cs b/src/Ombi.Store/Migrations/ExternalMySql/20210103205509_Jellyfin.Designer.cs similarity index 69% rename from src/Ombi.Store/Migrations/ExternalMySql/20201212163118_Jellyfin.Designer.cs rename to src/Ombi.Store/Migrations/ExternalMySql/20210103205509_Jellyfin.Designer.cs index 93c128c5c..72f8d2376 100644 --- a/src/Ombi.Store/Migrations/ExternalMySql/20201212163118_Jellyfin.Designer.cs +++ b/src/Ombi.Store/Migrations/ExternalMySql/20210103205509_Jellyfin.Designer.cs @@ -9,23 +9,24 @@ using Ombi.Store.Context.MySql; namespace Ombi.Store.Migrations.ExternalMySql { [DbContext(typeof(ExternalMySqlContext))] - [Migration("20201212014227_Jellyfin")] + [Migration("20210103205509_Jellyfin")] partial class Jellyfin { protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder + .HasAnnotation("Relational:MaxIdentifierLength", 64) .HasAnnotation("ProductVersion", "5.0.1"); modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TheMovieDbId") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.HasKey("Id"); @@ -36,35 +37,35 @@ namespace Ombi.Store.Migrations.ExternalMySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("AddedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EmbyId") .IsRequired() - .HasColumnType("TEXT"); + .HasColumnType("varchar(255)"); b.Property("ImdbId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("ProviderId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("TheMovieDbId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("Title") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("TvDbId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("Type") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Url") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.HasKey("Id"); @@ -75,37 +76,37 @@ namespace Ombi.Store.Migrations.ExternalMySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("AddedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EmbyId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("EpisodeNumber") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("ImdbId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("ParentId") - .HasColumnType("TEXT"); + .HasColumnType("varchar(255)"); b.Property("ProviderId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("SeasonNumber") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TheMovieDbId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("Title") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("TvDbId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.HasKey("Id"); @@ -118,35 +119,35 @@ namespace Ombi.Store.Migrations.ExternalMySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("AddedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ImdbId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("JellyfinId") .IsRequired() - .HasColumnType("TEXT"); + .HasColumnType("varchar(255)"); b.Property("ProviderId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("TheMovieDbId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("Title") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("TvDbId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("Type") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Url") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.HasKey("Id"); @@ -157,37 +158,37 @@ namespace Ombi.Store.Migrations.ExternalMySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("AddedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EpisodeNumber") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("ImdbId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("JellyfinId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("ParentId") - .HasColumnType("TEXT"); + .HasColumnType("varchar(255)"); b.Property("ProviderId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("SeasonNumber") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TheMovieDbId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("Title") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("TvDbId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.HasKey("Id"); @@ -200,31 +201,31 @@ namespace Ombi.Store.Migrations.ExternalMySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("AddedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ArtistId") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("ForeignAlbumId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("Monitored") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("PercentOfTracks") - .HasColumnType("TEXT"); + .HasColumnType("decimal(65,30)"); b.Property("ReleaseDate") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Title") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("TrackCount") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.HasKey("Id"); @@ -235,19 +236,19 @@ namespace Ombi.Store.Migrations.ExternalMySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("ArtistId") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("ArtistName") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("ForeignArtistId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("Monitored") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.HasKey("Id"); @@ -258,25 +259,25 @@ namespace Ombi.Store.Migrations.ExternalMySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("EpisodeNumber") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("GrandparentKey") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Key") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("ParentKey") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SeasonNumber") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Title") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.HasKey("Id"); @@ -289,22 +290,22 @@ namespace Ombi.Store.Migrations.ExternalMySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("ParentKey") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("PlexContentId") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("PlexServerContentId") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SeasonKey") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SeasonNumber") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.HasKey("Id"); @@ -317,40 +318,40 @@ namespace Ombi.Store.Migrations.ExternalMySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("AddedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ImdbId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("Key") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Quality") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("ReleaseYear") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("RequestId") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TheMovieDbId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("Title") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("TvDbId") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("Type") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Url") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.HasKey("Id"); @@ -361,13 +362,13 @@ namespace Ombi.Store.Migrations.ExternalMySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("HasFile") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("TheMovieDbId") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.HasKey("Id"); @@ -378,10 +379,10 @@ namespace Ombi.Store.Migrations.ExternalMySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TvDbId") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.HasKey("Id"); @@ -392,16 +393,16 @@ namespace Ombi.Store.Migrations.ExternalMySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("EpisodeNumber") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SeasonNumber") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TvDbId") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.HasKey("Id"); @@ -412,10 +413,10 @@ namespace Ombi.Store.Migrations.ExternalMySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TvDbId") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.HasKey("Id"); @@ -426,19 +427,19 @@ namespace Ombi.Store.Migrations.ExternalMySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("EpisodeNumber") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("HasFile") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("SeasonNumber") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TvDbId") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.HasKey("Id"); diff --git a/src/Ombi.Store/Migrations/ExternalMySql/20201212163118_Jellyfin.cs b/src/Ombi.Store/Migrations/ExternalMySql/20210103205509_Jellyfin.cs similarity index 52% rename from src/Ombi.Store/Migrations/ExternalMySql/20201212163118_Jellyfin.cs rename to src/Ombi.Store/Migrations/ExternalMySql/20210103205509_Jellyfin.cs index bb23c95c9..cd7fe85d5 100644 --- a/src/Ombi.Store/Migrations/ExternalMySql/20201212163118_Jellyfin.cs +++ b/src/Ombi.Store/Migrations/ExternalMySql/20210103205509_Jellyfin.cs @@ -12,17 +12,17 @@ namespace Ombi.Store.Migrations.ExternalMySql name: "JellyfinContent", columns: table => new { - Id = table.Column(nullable: false) + Id = table.Column(type: "int", nullable: false) .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - Title = table.Column(nullable: true), - ProviderId = table.Column(nullable: true), - JellyfinId = table.Column(nullable: false), - Type = table.Column(nullable: false), - AddedAt = table.Column(nullable: false), - ImdbId = table.Column(nullable: true), - TheMovieDbId = table.Column(nullable: true), - TvDbId = table.Column(nullable: true), - Url = table.Column(nullable: true) + Title = table.Column(type: "longtext", nullable: true), + ProviderId = table.Column(type: "longtext", nullable: true), + JellyfinId = table.Column(type: "varchar(255)", nullable: false), + Type = table.Column(type: "int", nullable: false), + AddedAt = table.Column(type: "datetime(6)", nullable: false), + ImdbId = table.Column(type: "longtext", nullable: true), + TheMovieDbId = table.Column(type: "longtext", nullable: true), + TvDbId = table.Column(type: "longtext", nullable: true), + Url = table.Column(type: "longtext", nullable: true) }, constraints: table => { @@ -34,18 +34,18 @@ namespace Ombi.Store.Migrations.ExternalMySql name: "JellyfinEpisode", columns: table => new { - Id = table.Column(nullable: false) + Id = table.Column(type: "int", nullable: false) .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - Title = table.Column(nullable: true), - JellyfinId = table.Column(nullable: true), - EpisodeNumber = table.Column(nullable: false), - SeasonNumber = table.Column(nullable: false), - ParentId = table.Column(nullable: true), - ProviderId = table.Column(nullable: true), - AddedAt = table.Column(nullable: false), - TvDbId = table.Column(nullable: true), - ImdbId = table.Column(nullable: true), - TheMovieDbId = table.Column(nullable: true) + Title = table.Column(type: "longtext", nullable: true), + JellyfinId = table.Column(type: "longtext", nullable: true), + EpisodeNumber = table.Column(type: "int", nullable: false), + SeasonNumber = table.Column(type: "int", nullable: false), + ParentId = table.Column(type: "varchar(255)", nullable: true), + ProviderId = table.Column(type: "longtext", nullable: true), + AddedAt = table.Column(type: "datetime(6)", nullable: false), + TvDbId = table.Column(type: "longtext", nullable: true), + ImdbId = table.Column(type: "longtext", nullable: true), + TheMovieDbId = table.Column(type: "longtext", nullable: true) }, constraints: table => { @@ -67,10 +67,10 @@ namespace Ombi.Store.Migrations.ExternalMySql protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "JellyfinContent"); + name: "JellyfinEpisode"); migrationBuilder.DropTable( - name: "JellyfinEpisode"); + name: "JellyfinContent"); } } } diff --git a/src/Ombi.Store/Migrations/ExternalMySql/ExternalMySqlContextModelSnapshot.cs b/src/Ombi.Store/Migrations/ExternalMySql/ExternalMySqlContextModelSnapshot.cs index 70312e542..00b274c52 100644 --- a/src/Ombi.Store/Migrations/ExternalMySql/ExternalMySqlContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/ExternalMySql/ExternalMySqlContextModelSnapshot.cs @@ -14,8 +14,8 @@ namespace Ombi.Store.Migrations.ExternalMySql { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.1.1") - .HasAnnotation("Relational:MaxIdentifierLength", 64); + .HasAnnotation("Relational:MaxIdentifierLength", 64) + .HasAnnotation("ProductVersion", "5.0.1"); modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => { @@ -42,28 +42,28 @@ namespace Ombi.Store.Migrations.ExternalMySql b.Property("EmbyId") .IsRequired() - .HasColumnType("varchar(255) CHARACTER SET utf8mb4"); + .HasColumnType("varchar(255)"); b.Property("ImdbId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("ProviderId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("TheMovieDbId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("Title") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("TvDbId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("Type") .HasColumnType("int"); b.Property("Url") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.HasKey("Id"); @@ -80,31 +80,31 @@ namespace Ombi.Store.Migrations.ExternalMySql .HasColumnType("datetime(6)"); b.Property("EmbyId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("EpisodeNumber") .HasColumnType("int"); b.Property("ImdbId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("ParentId") - .HasColumnType("varchar(255) CHARACTER SET utf8mb4"); + .HasColumnType("varchar(255)"); b.Property("ProviderId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("SeasonNumber") .HasColumnType("int"); b.Property("TheMovieDbId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("Title") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("TvDbId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.HasKey("Id"); @@ -123,29 +123,29 @@ namespace Ombi.Store.Migrations.ExternalMySql .HasColumnType("datetime(6)"); b.Property("ImdbId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("JellyfinId") .IsRequired() - .HasColumnType("varchar(255) CHARACTER SET utf8mb4"); + .HasColumnType("varchar(255)"); b.Property("ProviderId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("TheMovieDbId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("Title") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("TvDbId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("Type") .HasColumnType("int"); b.Property("Url") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.HasKey("Id"); @@ -159,34 +159,34 @@ namespace Ombi.Store.Migrations.ExternalMySql .HasColumnType("int"); b.Property("AddedAt") - .HasColumnType("dateime(6)"); + .HasColumnType("datetime(6)"); b.Property("EpisodeNumber") .HasColumnType("int"); b.Property("ImdbId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("JellyfinId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("ParentId") - .HasColumnType("varchar(255) CHARACTER SET utf8mb4"); + .HasColumnType("varchar(255)"); b.Property("ProviderId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("SeasonNumber") .HasColumnType("int"); b.Property("TheMovieDbId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("Title") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("TvDbId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.HasKey("Id"); @@ -208,7 +208,7 @@ namespace Ombi.Store.Migrations.ExternalMySql .HasColumnType("int"); b.Property("ForeignAlbumId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("Monitored") .HasColumnType("tinyint(1)"); @@ -220,7 +220,7 @@ namespace Ombi.Store.Migrations.ExternalMySql .HasColumnType("datetime(6)"); b.Property("Title") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("TrackCount") .HasColumnType("int"); @@ -240,10 +240,10 @@ namespace Ombi.Store.Migrations.ExternalMySql .HasColumnType("int"); b.Property("ArtistName") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("ForeignArtistId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("Monitored") .HasColumnType("tinyint(1)"); @@ -275,7 +275,7 @@ namespace Ombi.Store.Migrations.ExternalMySql .HasColumnType("int"); b.Property("Title") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.HasKey("Id"); @@ -322,34 +322,34 @@ namespace Ombi.Store.Migrations.ExternalMySql .HasColumnType("datetime(6)"); b.Property("ImdbId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("Key") .HasColumnType("int"); b.Property("Quality") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("ReleaseYear") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("RequestId") .HasColumnType("int"); b.Property("TheMovieDbId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("Title") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("TvDbId") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("Type") .HasColumnType("int"); b.Property("Url") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.HasKey("Id"); diff --git a/src/Ombi.sln b/src/Ombi.sln index 29decbc1c..658d37c3b 100644 --- a/src/Ombi.sln +++ b/src/Ombi.sln @@ -39,7 +39,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Schedule", "Ombi.Sched EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Emby", "Ombi.Api.Emby\Ombi.Api.Emby.csproj", "{08FF107D-31E1-470D-AF86-E09B015CEE06}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Jellyfin", "Ombi.Api.Jellyfin\Ombi.Api.Jellyfin.csproj", "{08FF107D-31E1-470D-AF86-E09B015CEE06}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Jellyfin", "Ombi.Api.Jellyfin\Ombi.Api.Jellyfin.csproj", "{F03757C7-5145-45C9-AFFF-B4E946755779}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Sonarr", "Ombi.Api.Sonarr\Ombi.Api.Sonarr.csproj", "{CFB5E008-D0D0-43C0-AA06-89E49D17F384}" EndProject @@ -171,6 +171,10 @@ Global {08FF107D-31E1-470D-AF86-E09B015CEE06}.Debug|Any CPU.Build.0 = Debug|Any CPU {08FF107D-31E1-470D-AF86-E09B015CEE06}.Release|Any CPU.ActiveCfg = Release|Any CPU {08FF107D-31E1-470D-AF86-E09B015CEE06}.Release|Any CPU.Build.0 = Release|Any CPU + {F03757C7-5145-45C9-AFFF-B4E946755779}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F03757C7-5145-45C9-AFFF-B4E946755779}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F03757C7-5145-45C9-AFFF-B4E946755779}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F03757C7-5145-45C9-AFFF-B4E946755779}.Release|Any CPU.Build.0 = Release|Any CPU {CFB5E008-D0D0-43C0-AA06-89E49D17F384}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CFB5E008-D0D0-43C0-AA06-89E49D17F384}.Debug|Any CPU.Build.0 = Debug|Any CPU {CFB5E008-D0D0-43C0-AA06-89E49D17F384}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.html b/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.html index f57f014c9..4802ccaaa 100644 --- a/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.html +++ b/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.html @@ -24,9 +24,9 @@ Season {{season.seasonNumber}} Season {{season.seasonNumber}} - +
diff --git a/src/Ombi/Controllers/V1/External/TesterController.cs b/src/Ombi/Controllers/V1/External/TesterController.cs index 0dbaac688..0c80b051c 100644 --- a/src/Ombi/Controllers/V1/External/TesterController.cs +++ b/src/Ombi/Controllers/V1/External/TesterController.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Api.CouchPotato; using Ombi.Api.Emby; +using Ombi.Api.Jellyfin; using Ombi.Api.Lidarr; using Ombi.Api.Plex; using Ombi.Api.Radarr; @@ -37,6 +38,7 @@ namespace Ombi.Controllers.V1.External [Produces("application/json")] public class TesterController : Controller { + /// /// Initializes a new instance of the class. /// @@ -44,7 +46,8 @@ namespace Ombi.Controllers.V1.External IPushbulletNotification pushbullet, ISlackNotification slack, IPushoverNotification po, IMattermostNotification mm, IPlexApi plex, IEmbyApiFactory emby, IRadarrApi radarr, ISonarrApi sonarr, ILogger log, IEmailProvider provider, ICouchPotatoApi cpApi, ITelegramNotification telegram, ISickRageApi srApi, INewsletterJob newsletter, ILegacyMobileNotification mobileNotification, - ILidarrApi lidarrApi, IGotifyNotification gotifyNotification, IWhatsAppApi whatsAppApi, OmbiUserManager um, IWebhookNotification webhookNotification) + ILidarrApi lidarrApi, IGotifyNotification gotifyNotification, IWhatsAppApi whatsAppApi, OmbiUserManager um, IWebhookNotification webhookNotification, + IJellyfinApi jellyfinApi) { Service = service; DiscordNotification = notification; @@ -69,6 +72,7 @@ namespace Ombi.Controllers.V1.External WhatsAppApi = whatsAppApi; UserManager = um; WebhookNotification = webhookNotification; + _jellyfinApi = jellyfinApi; } private INotificationService Service { get; } @@ -93,7 +97,8 @@ namespace Ombi.Controllers.V1.External private ILegacyMobileNotification MobileNotification { get; } private ILidarrApi LidarrApi { get; } private IWhatsAppApi WhatsAppApi { get; } - private OmbiUserManager UserManager {get;} + private OmbiUserManager UserManager {get; } + private readonly IJellyfinApi _jellyfinApi; /// /// Sends a test message to discord using the provided settings @@ -333,6 +338,26 @@ namespace Ombi.Controllers.V1.External } } + /// + /// Checks if we can connect to Jellyfin with the provided settings + /// + /// + /// + [HttpPost("jellyfin")] + public async Task Jellyfin([FromBody] JellyfinServers settings) + { + try + { + var result = await _jellyfinApi.GetUsers(settings.FullUri, settings.ApiKey); + return result.Any(); + } + catch (Exception e) + { + Log.LogError(LoggingEvents.Api, e, "Could not test Jellyfin"); + return false; + } + } + /// /// Checks if we can connect to Radarr with the provided settings /// diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index 2c615f57c..0b86b06f1 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -53,6 +53,10 @@ + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index 665811489..4108bb848 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -92,6 +92,7 @@ "TheatricalRelease": "Theatrical Release: {{date}}", "ViewOnPlex": "View On Plex", "ViewOnEmby": "View On Emby", + "ViewOnJellyfin": "View On Jellyfin", "RequestAdded": "Request for {{title}} has been added successfully", "Similar": "Similar", "Refine": "Refine", From b888a51b5b9b1b22a2887c0572aff4f48bb3735a Mon Sep 17 00:00:00 2001 From: tidusjar Date: Sun, 3 Jan 2021 21:43:53 +0000 Subject: [PATCH 59/82] last tweak --- .../src/app/settings/jellyfin/jellyfin.component.html | 4 ++-- .../ClientApp/src/app/settings/jellyfin/jellyfin.component.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.html b/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.html index 4ff8991c1..66b4b39f6 100644 --- a/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.html +++ b/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.html @@ -58,7 +58,7 @@
-
+
Base URL @@ -71,7 +71,7 @@ Current URL: "{{server.serverHostname}}/#!/itemdetails.html?id=1" - Current URL: "https://app.jellyfin.media/#!/itemdetails.html?id=1 + Current URL: "{{server.ssl ? "https://" : "http://"}}{{server.ip}}:{{server.port}}/"
diff --git a/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.ts b/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.ts index 2ae0d331d..133ae4b39 100644 --- a/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.ts +++ b/src/Ombi/ClientApp/src/app/settings/jellyfin/jellyfin.component.ts @@ -15,6 +15,7 @@ export class JellyfinComponent implements OnInit { public hasDiscoveredOrDirty: boolean; selected = new FormControl(0); + constructor(private settingsService: SettingsService, private notificationService: NotificationService, private testerService: TesterService, From 2e00aa00f087861c1e7416bb8e9e6a4ad2ebb85c Mon Sep 17 00:00:00 2001 From: tidusjar Date: Mon, 4 Jan 2021 09:00:41 +0000 Subject: [PATCH 60/82] fixed unit test --- .../Rule/Search/JellyfinAvailabilityRuleTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Ombi.Core.Tests/Rule/Search/JellyfinAvailabilityRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/JellyfinAvailabilityRuleTests.cs index 5da69a181..1b838f102 100644 --- a/src/Ombi.Core.Tests/Rule/Search/JellyfinAvailabilityRuleTests.cs +++ b/src/Ombi.Core.Tests/Rule/Search/JellyfinAvailabilityRuleTests.cs @@ -84,6 +84,8 @@ namespace Ombi.Core.Tests.Rule.Search { new JellyfinServers { + Ip = "8080", + Port = 9090, ServerHostname = string.Empty, ServerId = "8" } From 8220f41e0b9a4fbad56143c0e9cbd1d169802dc3 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Mon, 4 Jan 2021 14:14:24 +0000 Subject: [PATCH 61/82] * Added the ability to request on behalf of a user * Moved the "advanced" section into a small cog icon on the media details page * Added some more information on the movie panel e.g. Requested By user --- src/Ombi.Core/Engine/MovieRequestEngine.cs | 32 ++- src/Ombi.Core/Engine/TvRequestEngine.cs | 34 ++- .../Models/Requests/MovieRequestViewModel.cs | 1 + .../Models/Requests/TvRequestViewModel.cs | 2 + src/Ombi.Core/Models/UI/UserViewModel.cs | 6 + .../card/discover-card-details.component.ts | 2 +- .../discover-collections.component.ts | 2 +- .../grid/discover-grid.component.ts | 6 +- .../ClientApp/src/app/interfaces/IRadarr.ts | 7 +- .../src/app/interfaces/IRequestModel.ts | 1 + .../src/app/interfaces/ISearchTvResult.ts | 1 + .../ClientApp/src/app/interfaces/IUser.ts | 5 + .../src/app/media-details/components/index.ts | 10 +- .../movie/movie-details.component.html | 29 ++- .../movie/movie-details.component.ts | 35 ++- .../movie-admin-panel.component.html | 3 - .../movie-admin-panel.component.ts | 76 ------- .../movie-advanced-options.component.html | 4 +- .../movie-advanced-options.component.ts | 53 ++++- .../movie-information-panel.component.html | 20 +- .../request-behalf.component.html | 22 ++ .../request-behalf.component.ts | 52 +++++ .../tv-admin-panel.component.html | 3 - .../tv-admin-panel.component.ts | 83 ------- .../tv-advanced-options.component.html | 23 ++ .../tv-advanced-options.component.ts | 55 +++++ .../tv-information-panel.component.html | 4 +- .../components/tv/tv-details.component.html | 206 ++++++++++-------- .../components/tv/tv-details.component.ts | 39 +++- .../media-details.component.scss | 4 + .../app/media-details/media-details.module.ts | 4 +- .../src/app/services/identity.service.ts | 6 +- .../episode-request.component.html | 4 +- .../episode-request.component.ts | 33 +-- src/Ombi/ClientApp/src/styles/shared.scss | 6 +- src/Ombi/Controllers/V1/IdentityController.cs | 25 +++ src/Ombi/wwwroot/translations/en.json | 6 +- 37 files changed, 563 insertions(+), 341 deletions(-) delete mode 100644 src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-admin-panel/movie-admin-panel.component.html delete mode 100644 src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-admin-panel/movie-admin-panel.component.ts create mode 100644 src/Ombi/ClientApp/src/app/media-details/components/shared/request-behalf/request-behalf.component.html create mode 100644 src/Ombi/ClientApp/src/app/media-details/components/shared/request-behalf/request-behalf.component.ts delete mode 100644 src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-admin-panel/tv-admin-panel.component.html delete mode 100644 src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-admin-panel/tv-admin-panel.component.ts create mode 100644 src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-advanced-options/tv-advanced-options.component.html create mode 100644 src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-advanced-options/tv-advanced-options.component.ts diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index 741503795..c9dbde067 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -67,6 +67,22 @@ namespace Ombi.Core.Engine $"{movieInfo.Title}{(!string.IsNullOrEmpty(movieInfo.ReleaseDate) ? $" ({DateTime.Parse(movieInfo.ReleaseDate).Year})" : string.Empty)}"; var userDetails = await GetUser(); + var canRequestOnBehalf = false; + + if (model.RequestOnBehalf.HasValue()) + { + canRequestOnBehalf = await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(userDetails, OmbiRoles.Admin); + + if (!canRequestOnBehalf) + { + return new RequestEngineResult + { + Result = false, + Message = "You do not have the correct permissions to request on behalf of users!", + ErrorMessage = $"You do not have the correct permissions to request on behalf of users!" + }; + } + } var requestModel = new MovieRequests { @@ -82,7 +98,7 @@ namespace Ombi.Core.Engine Status = movieInfo.Status, RequestedDate = DateTime.UtcNow, Approved = false, - RequestedUserId = userDetails.Id, + RequestedUserId = canRequestOnBehalf ? model.RequestOnBehalf : userDetails.Id, Background = movieInfo.BackdropPath, LangCode = model.LanguageCode, RequestedByAlias = model.RequestedByAlias @@ -103,7 +119,7 @@ namespace Ombi.Core.Engine if (requestModel.Approved) // The rules have auto approved this { - var requestEngineResult = await AddMovieRequest(requestModel, fullMovieName); + var requestEngineResult = await AddMovieRequest(requestModel, fullMovieName, model.RequestOnBehalf); if (requestEngineResult.Result) { var result = await ApproveMovie(requestModel); @@ -124,7 +140,7 @@ namespace Ombi.Core.Engine // If there are no providers then it's successful but movie has not been sent } - return await AddMovieRequest(requestModel, fullMovieName); + return await AddMovieRequest(requestModel, fullMovieName, model.RequestOnBehalf); } @@ -270,7 +286,7 @@ namespace Ombi.Core.Engine allRequests = allRequests.Where(x => x.Available); break; case RequestStatus.Denied: - allRequests = allRequests.Where(x => x.Denied.HasValue && x.Denied.Value && !x.Available); + allRequests = allRequests.Where(x => x.Denied.HasValue && x.Denied.Value && !x.Available); break; default: break; @@ -429,7 +445,7 @@ namespace Ombi.Core.Engine public async Task GetRequest(int requestId) { var request = await MovieRepository.GetWithUser().Where(x => x.Id == requestId).FirstOrDefaultAsync(); - await CheckForSubscription(new HideResult(), new List{request }); + await CheckForSubscription(new HideResult(), new List { request }); return request; } @@ -654,19 +670,19 @@ namespace Ombi.Core.Engine }; } - private async Task AddMovieRequest(MovieRequests model, string movieName) + private async Task AddMovieRequest(MovieRequests model, string movieName, string requestOnBehalf) { await MovieRepository.Add(model); var result = await RunSpecificRule(model, SpecificRules.CanSendNotification); if (result.Success) - { + { await NotificationHelper.NewRequest(model); } await _requestLog.Add(new RequestLog { - UserId = (await GetUser()).Id, + UserId = requestOnBehalf.HasValue() ? requestOnBehalf : (await GetUser()).Id, RequestDate = DateTime.UtcNow, RequestId = model.Id, RequestType = RequestType.Movie, diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index e066261f4..b06c99d49 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -51,12 +51,28 @@ namespace Ombi.Core.Engine public async Task RequestTvShow(TvRequestViewModel tv) { var user = await GetUser(); + var canRequestOnBehalf = false; + + if (tv.RequestOnBehalf.HasValue()) + { + canRequestOnBehalf = await UserManager.IsInRoleAsync(user, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(user, OmbiRoles.Admin); + + if (!canRequestOnBehalf) + { + return new RequestEngineResult + { + Result = false, + Message = "You do not have the correct permissions to request on behalf of users!", + ErrorMessage = $"You do not have the correct permissions to request on behalf of users!" + }; + } + } var tvBuilder = new TvShowRequestBuilder(TvApi, MovieDbApi); (await tvBuilder .GetShowInfo(tv.TvDbId)) .CreateTvList(tv) - .CreateChild(tv, user.Id); + .CreateChild(tv, canRequestOnBehalf ? tv.RequestOnBehalf : user.Id); await tvBuilder.BuildEpisodes(tv); @@ -124,12 +140,12 @@ namespace Ombi.Core.Engine ErrorMessage = "This has already been requested" }; } - return await AddExistingRequest(tvBuilder.ChildRequest, existingRequest); + return await AddExistingRequest(tvBuilder.ChildRequest, existingRequest, tv.RequestOnBehalf); } // This is a new request var newRequest = tvBuilder.CreateNewRequest(tv); - return await AddRequest(newRequest.NewRequest); + return await AddRequest(newRequest.NewRequest, tv.RequestOnBehalf); } public async Task> GetRequests(int count, int position, OrderFilterModel type) @@ -736,21 +752,21 @@ namespace Ombi.Core.Engine } } - private async Task AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest) + private async Task AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest, string requestOnBehalf) { // Add the child existingRequest.ChildRequests.Add(newRequest); await TvRepository.Update(existingRequest); - return await AfterRequest(newRequest); + return await AfterRequest(newRequest, requestOnBehalf); } - private async Task AddRequest(TvRequests model) + private async Task AddRequest(TvRequests model, string requestOnBehalf) { await TvRepository.Add(model); // This is a new request so we should only have 1 child - return await AfterRequest(model.ChildRequests.FirstOrDefault()); + return await AfterRequest(model.ChildRequests.FirstOrDefault(), requestOnBehalf); } private static List SortEpisodes(List items) @@ -766,7 +782,7 @@ namespace Ombi.Core.Engine } - private async Task AfterRequest(ChildRequests model) + private async Task AfterRequest(ChildRequests model, string requestOnBehalf) { var sendRuleResult = await RunSpecificRule(model, SpecificRules.CanSendNotification); if (sendRuleResult.Success) @@ -776,7 +792,7 @@ namespace Ombi.Core.Engine await _requestLog.Add(new RequestLog { - UserId = (await GetUser()).Id, + UserId = requestOnBehalf.HasValue() ? requestOnBehalf : (await GetUser()).Id, RequestDate = DateTime.UtcNow, RequestId = model.Id, RequestType = RequestType.TvShow, diff --git a/src/Ombi.Core/Models/Requests/MovieRequestViewModel.cs b/src/Ombi.Core/Models/Requests/MovieRequestViewModel.cs index 5a79d2982..22d1cc449 100644 --- a/src/Ombi.Core/Models/Requests/MovieRequestViewModel.cs +++ b/src/Ombi.Core/Models/Requests/MovieRequestViewModel.cs @@ -33,6 +33,7 @@ namespace Ombi.Core.Models.Requests { public int TheMovieDbId { get; set; } public string LanguageCode { get; set; } = "en"; + public string RequestOnBehalf { get; set; } /// /// This is only set from a HTTP Header diff --git a/src/Ombi.Core/Models/Requests/TvRequestViewModel.cs b/src/Ombi.Core/Models/Requests/TvRequestViewModel.cs index c17925b1b..15349462b 100644 --- a/src/Ombi.Core/Models/Requests/TvRequestViewModel.cs +++ b/src/Ombi.Core/Models/Requests/TvRequestViewModel.cs @@ -12,6 +12,8 @@ namespace Ombi.Core.Models.Requests public List Seasons { get; set; } = new List(); [JsonIgnore] public string RequestedByAlias { get; set; } + + public string RequestOnBehalf { get; set; } } public class SeasonsViewModel diff --git a/src/Ombi.Core/Models/UI/UserViewModel.cs b/src/Ombi.Core/Models/UI/UserViewModel.cs index e74e5037d..0c3c9e349 100644 --- a/src/Ombi.Core/Models/UI/UserViewModel.cs +++ b/src/Ombi.Core/Models/UI/UserViewModel.cs @@ -30,4 +30,10 @@ namespace Ombi.Core.Models.UI public string Value { get; set; } public bool Enabled { get; set; } } + + public class UserViewModelDropdown + { + public string Id { get; set; } + public string Username { get; set; } + } } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.ts b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.ts index fa13b1866..f3d1276ad 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.ts @@ -67,7 +67,7 @@ export class DiscoverCardDetailsComponent implements OnInit { public async request() { this.loading = true; if (this.data.type === RequestType.movie) { - const result = await this.requestService.requestMovie({ theMovieDbId: this.data.id, languageCode: "" }).toPromise(); + const result = await this.requestService.requestMovie({ theMovieDbId: this.data.id, languageCode: "", requestOnBehalf: null }).toPromise(); this.loading = false; if (result.result) { diff --git a/src/Ombi/ClientApp/src/app/discover/components/collections/discover-collections.component.ts b/src/Ombi/ClientApp/src/app/discover/components/collections/discover-collections.component.ts index 9008dd8ca..dd634ef92 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/collections/discover-collections.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/collections/discover-collections.component.ts @@ -34,7 +34,7 @@ export class DiscoverCollectionsComponent implements OnInit { public async requestCollection() { await this.collection.collection.forEach(async (movie) => { - await this.requestService.requestMovie({theMovieDbId: movie.id, languageCode: null}).toPromise(); + await this.requestService.requestMovie({theMovieDbId: movie.id, languageCode: null, requestOnBehalf: null}).toPromise(); }); this.messageService.send("Requested Collection"); } diff --git a/src/Ombi/ClientApp/src/app/discover/components/grid/discover-grid.component.ts b/src/Ombi/ClientApp/src/app/discover/components/grid/discover-grid.component.ts index 4c28c1174..42f371dc2 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/grid/discover-grid.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/grid/discover-grid.component.ts @@ -5,7 +5,7 @@ import { ImageService, RequestService, SearchV2Service } from "../../../services import { MatDialog } from "@angular/material/dialog"; import { ISearchTvResultV2 } from "../../../interfaces/ISearchTvResultV2"; import { ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2"; -import { EpisodeRequestComponent } from "../../../shared/episode-request/episode-request.component"; +import { EpisodeRequestComponent, EpisodeRequestData } from "../../../shared/episode-request/episode-request.component"; import { MatSnackBar } from "@angular/material/snack-bar"; import { Router } from "@angular/router"; import { DomSanitizer } from "@angular/platform-browser"; @@ -139,7 +139,7 @@ export class DiscoverGridComponent implements OnInit { public async request() { this.requesting = true; if (this.result.type === RequestType.movie) { - const result = await this.requestService.requestMovie({ theMovieDbId: this.result.id, languageCode: "" }).toPromise(); + const result = await this.requestService.requestMovie({ theMovieDbId: this.result.id, languageCode: "", requestOnBehalf: null }).toPromise(); if (result.result) { this.result.requested = true; @@ -148,7 +148,7 @@ export class DiscoverGridComponent implements OnInit { this.notification.open(result.errorMessage, "Ok"); } } else if (this.result.type === RequestType.tvShow) { - this.dialog.open(EpisodeRequestComponent, { width: "700px", data: this.tv, panelClass: 'modal-panel' }) + this.dialog.open(EpisodeRequestComponent, { width: "700px", data: { series: this.tv, requestOnBehalf: null }, panelClass: 'modal-panel' }) } this.requesting = false; } diff --git a/src/Ombi/ClientApp/src/app/interfaces/IRadarr.ts b/src/Ombi/ClientApp/src/app/interfaces/IRadarr.ts index a52b19d9d..e01d74cd5 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/IRadarr.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/IRadarr.ts @@ -1,4 +1,7 @@ -export interface IRadarrRootFolder { +import { IChildRequests, IMovieRequests } from "."; +import { ITvRequests } from "./IRequestModel"; + +export interface IRadarrRootFolder { id: number; path: string; } @@ -24,4 +27,6 @@ export interface IAdvancedData { rootFolder: IRadarrRootFolder; rootFolders: IRadarrRootFolder[]; rootFolderId: number; + movieRequest: IMovieRequests; + tvRequest: ITvRequests; } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/interfaces/IRequestModel.ts b/src/Ombi/ClientApp/src/app/interfaces/IRequestModel.ts index 4efb5ee76..1ee92f336 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/IRequestModel.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/IRequestModel.ts @@ -167,6 +167,7 @@ export interface IEpisodesRequests { export interface IMovieRequestModel { theMovieDbId: number; languageCode: string | undefined; + requestOnBehalf: string | undefined; } export interface IFilter { diff --git a/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResult.ts b/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResult.ts index 477cc6dcd..73533d7c6 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResult.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResult.ts @@ -46,6 +46,7 @@ export interface ITvRequestViewModel { latestSeason: boolean; tvDbId: number; seasons: ISeasonsViewModel[]; + requestOnBehalf: string | undefined; } export interface ISeasonsViewModel { diff --git a/src/Ombi/ClientApp/src/app/interfaces/IUser.ts b/src/Ombi/ClientApp/src/app/interfaces/IUser.ts index 4235d55cf..f03238b7c 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/IUser.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/IUser.ts @@ -24,6 +24,11 @@ export interface IUser { musicRequestQuota: IRemainingRequests | null; } +export interface IUserDropdown { + username: string; + id: string; +} + export interface IUserQualityProfiles { sonarrQualityProfileAnime: number; sonarrRootPathAnime: number; diff --git a/src/Ombi/ClientApp/src/app/media-details/components/index.ts b/src/Ombi/ClientApp/src/app/media-details/components/index.ts index 001eb20bf..52892ade2 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/index.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/index.ts @@ -9,7 +9,6 @@ import { MediaPosterComponent } from "./shared/media-poster/media-poster.compone import { CastCarouselComponent } from "./shared/cast-carousel/cast-carousel.component"; import { DenyDialogComponent } from "./shared/deny-dialog/deny-dialog.component"; import { TvRequestsPanelComponent } from "./tv/panels/tv-requests/tv-requests-panel.component"; -import { MovieAdminPanelComponent } from "./movie/panels/movie-admin-panel/movie-admin-panel.component"; import { MovieAdvancedOptionsComponent } from "./movie/panels/movie-advanced-options/movie-advanced-options.component"; import { SearchService, RequestService, RadarrService, IssuesService, SonarrService } from "../../services"; import { RequestServiceV2 } from "../../services/requestV2.service"; @@ -18,7 +17,8 @@ import { ArtistDetailsComponent } from "./artist/artist-details.component"; import { ArtistInformationPanel } from "./artist/panels/artist-information-panel/artist-information-panel.component"; import { ArtistReleasePanel } from "./artist/panels/artist-release-panel/artist-release-panel.component"; import { IssuesPanelComponent } from "./shared/issues-panel/issues-panel.component"; -import { TvAdminPanelComponent } from "./tv/panels/tv-admin-panel/tv-admin-panel.component"; +import { TvAdvancedOptionsComponent } from "./tv/panels/tv-advanced-options/tv-advanced-options.component"; +import { RequestBehalfComponent } from "./shared/request-behalf/request-behalf.component"; export const components: any[] = [ MovieDetailsComponent, @@ -32,21 +32,23 @@ export const components: any[] = [ CastCarouselComponent, DenyDialogComponent, TvRequestsPanelComponent, - MovieAdminPanelComponent, MovieAdvancedOptionsComponent, + TvAdvancedOptionsComponent, NewIssueComponent, ArtistDetailsComponent, ArtistInformationPanel, ArtistReleasePanel, + RequestBehalfComponent, IssuesPanelComponent, - TvAdminPanelComponent, ]; export const entryComponents: any[] = [ YoutubeTrailerComponent, DenyDialogComponent, MovieAdvancedOptionsComponent, + TvAdvancedOptionsComponent, NewIssueComponent, + RequestBehalfComponent, ]; export const providers: any[] = [ diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html index 375ca40ec..933c6ef2c 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html @@ -21,7 +21,7 @@
-
+
@@ -64,23 +64,28 @@ +
- - - + +
+ + + + +
-
- - - - - - - diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts index 55a69fe15..56b9d3ffe 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts @@ -1,5 +1,5 @@ import { Component, ViewEncapsulation } from "@angular/core"; -import { ImageService, SearchV2Service, RequestService, MessageService } from "../../../services"; +import { ImageService, SearchV2Service, RequestService, MessageService, RadarrService } from "../../../services"; import { ActivatedRoute } from "@angular/router"; import { DomSanitizer } from "@angular/platform-browser"; import { ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2"; @@ -10,6 +10,9 @@ import { IMovieRequests, RequestType, IAdvancedData } from "../../../interfaces" import { DenyDialogComponent } from "../shared/deny-dialog/deny-dialog.component"; import { NewIssueComponent } from "../shared/new-issue/new-issue.component"; import { StorageService } from "../../../shared/storage/storage-service"; +import { MovieAdvancedOptionsComponent } from "./panels/movie-advanced-options/movie-advanced-options.component"; +import { RequestServiceV2 } from "../../../services/requestV2.service"; +import { RequestBehalfComponent } from "../shared/request-behalf/request-behalf.component"; @Component({ templateUrl: "./movie-details.component.html", @@ -30,6 +33,7 @@ export class MovieDetailsComponent { constructor(private searchService: SearchV2Service, private route: ActivatedRoute, private sanitizer: DomSanitizer, private imageService: ImageService, public dialog: MatDialog, private requestService: RequestService, + private requestService2: RequestServiceV2, private radarrService: RadarrService, public messageService: MessageService, private auth: AuthService, private storage: StorageService) { this.route.params.subscribe((params: any) => { @@ -47,6 +51,10 @@ export class MovieDetailsComponent { this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); + if (this.isAdmin) { + this.showAdvanced = await this.radarrService.isRadarrEnabled(); + } + if (this.imdbId) { this.searchService.getMovieByImdbId(this.imdbId).subscribe(async x => { this.movie = x; @@ -76,8 +84,8 @@ export class MovieDetailsComponent { } } - public async request() { - const result = await this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, languageCode: null }).toPromise(); + public async request(userId?: string) { + const result = await this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, languageCode: null, requestOnBehalf: userId }).toPromise(); if (result.result) { this.movie.requested = true; this.messageService.send(result.message, "Ok"); @@ -144,4 +152,25 @@ export class MovieDetailsComponent { this.movieRequest.rootPathOverrideTitle = data.profiles.filter(x => x.id == data.profileId)[0].name; } } + + public async openAdvancedOptions() { + const dialog = this.dialog.open(MovieAdvancedOptionsComponent, { width: "700px", data: { movieRequest: this.movieRequest }, panelClass: 'modal-panel' }) + await dialog.afterClosed().subscribe(async result => { + if (result) { + result.rootFolder = result.rootFolders.filter(f => f.id === +result.rootFolderId)[0]; + result.profile = result.profiles.filter(f => f.id === +result.profileId)[0]; + await this.requestService2.updateMovieAdvancedOptions({ qualityOverride: result.profileId, rootPathOverride: result.rootFolderId, requestId: this.movieRequest.id }).toPromise(); + this.setAdvancedOptions(result); + } + }); + } + + public async openRequestOnBehalf() { + const dialog = this.dialog.open(RequestBehalfComponent, { width: "700px", panelClass: 'modal-panel' }) + await dialog.afterClosed().subscribe(async result => { + if (result) { + await this.request(result.id); + } + }); + } } diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-admin-panel/movie-admin-panel.component.html b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-admin-panel/movie-admin-panel.component.html deleted file mode 100644 index 94bb66a12..000000000 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-admin-panel/movie-admin-panel.component.html +++ /dev/null @@ -1,3 +0,0 @@ -
- -
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-admin-panel/movie-admin-panel.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-admin-panel/movie-admin-panel.component.ts deleted file mode 100644 index e19840a6d..000000000 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-admin-panel/movie-admin-panel.component.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { Component, Input, OnInit, EventEmitter, Output } from "@angular/core"; -import { RadarrService } from "../../../../../services"; -import { IRadarrProfile, IRadarrRootFolder, IMovieRequests, IAdvancedData } from "../../../../../interfaces"; -import { MatDialog } from "@angular/material/dialog"; -import { MovieAdvancedOptionsComponent } from "../movie-advanced-options/movie-advanced-options.component"; -import { RequestServiceV2 } from "../../../../../services/requestV2.service"; - -@Component({ - templateUrl: "./movie-admin-panel.component.html", - selector: "movie-admin-panel", -}) -export class MovieAdminPanelComponent implements OnInit { - - @Input() public movie: IMovieRequests; - @Output() public advancedOptionsChanged = new EventEmitter(); - @Output() public radarrEnabledChange = new EventEmitter(); - - public radarrEnabled: boolean; - public radarrProfiles: IRadarrProfile[]; - public selectedRadarrProfile: IRadarrProfile; - public radarrRootFolders: IRadarrRootFolder[]; - public selectRadarrRootFolders: IRadarrRootFolder; - - constructor(private radarrService: RadarrService, private requestService: RequestServiceV2, private dialog: MatDialog) { } - - public async ngOnInit() { - this.radarrEnabled = await this.radarrService.isRadarrEnabled(); - if (this.radarrEnabled) { - this.radarrService.getQualityProfilesFromSettings().subscribe(c => { - this.radarrProfiles = c; - this.setQualityOverrides(); - }); - this.radarrService.getRootFoldersFromSettings().subscribe(c => { - this.radarrRootFolders = c; - this.setRootFolderOverrides(); - }); - } - - this.radarrEnabledChange.emit(this.radarrEnabled); - } - - public async openAdvancedOptions() { - const dialog = this.dialog.open(MovieAdvancedOptionsComponent, { width: "700px", data: { profiles: this.radarrProfiles, rootFolders: this.radarrRootFolders }, panelClass: 'modal-panel' }) - await dialog.afterClosed().subscribe(async result => { - if(result) { - // get the name and ids - result.rootFolder = result.rootFolders.filter(f => f.id === +result.rootFolderId)[0]; - result.profile = result.profiles.filter(f => f.id === +result.profileId)[0]; - await this.requestService.updateMovieAdvancedOptions({qualityOverride: result.profileId, rootPathOverride: result.rootFolderId, requestId: this.movie.id}).toPromise(); - this.advancedOptionsChanged.emit(result); - } - }); - } - - private setQualityOverrides(): void { - if (this.radarrProfiles) { - const profile = this.radarrProfiles.filter((p) => { - return p.id === this.movie.qualityOverride; - }); - if (profile.length > 0) { - this.movie.qualityOverrideTitle = profile[0].name; - } - } - } - - private setRootFolderOverrides(): void { - if (this.radarrRootFolders) { - const path = this.radarrRootFolders.filter((folder) => { - return folder.id === this.movie.rootPathOverride; - }); - if (path.length > 0) { - this.movie.rootPathOverrideTitle = path[0].path; - } - } - } -} diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-advanced-options/movie-advanced-options.component.html b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-advanced-options/movie-advanced-options.component.html index 8caecdfdf..e839b49b1 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-advanced-options/movie-advanced-options.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-advanced-options/movie-advanced-options.component.html @@ -5,7 +5,7 @@ {{'MediaDetails.QualityProfilesSelect' | translate }} - {{profile.name}} + {{profile.name}}
@@ -13,7 +13,7 @@ {{'MediaDetails.RootFolderSelect' | translate }} - {{profile.path}} + {{profile.path}}
diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-advanced-options/movie-advanced-options.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-advanced-options/movie-advanced-options.component.ts index d5c3310fb..d164644ff 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-advanced-options/movie-advanced-options.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-advanced-options/movie-advanced-options.component.ts @@ -1,14 +1,55 @@ -import { Component, Inject } from "@angular/core"; +import { Component, Inject, OnInit } from "@angular/core"; import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; -import { IAdvancedData } from "../../../../../interfaces"; +import { IAdvancedData, IRadarrProfile, IRadarrRootFolder } from "../../../../../interfaces"; +import { RadarrService } from "../../../../../services"; @Component({ templateUrl: "./movie-advanced-options.component.html", selector: "movie-advanced-options", }) -export class MovieAdvancedOptionsComponent { - +export class MovieAdvancedOptionsComponent implements OnInit { + + public radarrProfiles: IRadarrProfile[]; + public radarrRootFolders: IRadarrRootFolder[]; + constructor(public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: IAdvancedData, - ) { - } + private radarrService: RadarrService + ) { + } + + + public async ngOnInit() { + this.radarrService.getQualityProfilesFromSettings().subscribe(c => { + this.radarrProfiles = c; + this.data.profiles = c; + this.setQualityOverrides(); + }); + this.radarrService.getRootFoldersFromSettings().subscribe(c => { + this.radarrRootFolders = c; + this.data.rootFolders = c; + this.setRootFolderOverrides(); + }); + } + + private setQualityOverrides(): void { + if (this.radarrProfiles) { + const profile = this.radarrProfiles.filter((p) => { + return p.id === this.data.movieRequest.qualityOverride; + }); + if (profile.length > 0) { + this.data.movieRequest.qualityOverrideTitle = profile[0].name; + } + } + } + + private setRootFolderOverrides(): void { + if (this.radarrRootFolders) { + const path = this.radarrRootFolders.filter((folder) => { + return folder.id === this.data.movieRequest.rootPathOverride; + }); + if (path.length > 0) { + this.data.movieRequest.rootPathOverrideTitle = path[0].path; + } + } + } } diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html index 449dd09c4..3f838d9ee 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html @@ -9,7 +9,7 @@
{{'Common.NotAvailable' | translate}}
- +
{{'MediaDetails.RequestStatus' | translate }}
{{'Common.ProcessingRequest' | translate}}
@@ -18,16 +18,28 @@
{{'Common.NotRequested' | translate}}
+ +
+ {{'Requests.RequestedBy' | translate }} +
{{request.requestedUser.userAlias}}
+
+ +
+ {{'Requests.RequestDate' | translate }} +
{{request.requestedDate | date}}
+
+ +
{{'MediaDetails.Quality' | translate }}:
{{movie.quality | quality}}
-
+
{{'MediaDetails.RootFolderOverride' | translate }}
{{request.rootPathOverrideTitle}}
-
+
{{'MediaDetails.QualityOverride' | translate }}
{{request.qualityOverrideTitle}}
@@ -81,7 +93,7 @@
-
+
{{'MediaDetails.Keywords' | translate }}: diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/request-behalf/request-behalf.component.html b/src/Ombi/ClientApp/src/app/media-details/components/shared/request-behalf/request-behalf.component.html new file mode 100644 index 000000000..db7acde9d --- /dev/null +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/request-behalf/request-behalf.component.html @@ -0,0 +1,22 @@ +

{{ 'MediaDetails.RequestOnBehalf' | translate}}

+
+
+ + + + + {{option.username}} + + + + +
+
+ + +
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/request-behalf/request-behalf.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/shared/request-behalf/request-behalf.component.ts new file mode 100644 index 000000000..da684db06 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/request-behalf/request-behalf.component.ts @@ -0,0 +1,52 @@ +import { Component, Inject, OnInit } from "@angular/core"; +import { IDenyDialogData } from "../interfaces/interfaces"; +import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; +import { RequestService, MessageService, IdentityService } from "../../../../services"; +import { RequestType, IRequestEngineResult, IUserDropdown } from "../../../../interfaces"; +import { FormControl } from "@angular/forms"; +import { Observable } from "rxjs"; +import { filter, map, startWith } from "rxjs/operators"; + + + +@Component({ + selector: "request-behalf", + templateUrl: "./request-behalf.component.html", +}) +export class RequestBehalfComponent implements OnInit { + constructor( + public dialogRef: MatDialogRef, + public identity: IdentityService) { } + + public myControl = new FormControl(); + public options: IUserDropdown[]; + public filteredOptions: Observable; + public userId: string; + + public async ngOnInit() { + this.options = await this.identity.getUsersDropdown().toPromise(); + this.filteredOptions = this.myControl.valueChanges + .pipe( + startWith(''), + map(value => this._filter(value)) + ); + } + + public request() { + this.dialogRef.close(this.myControl.value); + } + + public onNoClick(): void { + this.dialogRef.close(); + } + + public displayFn(user: IUserDropdown): string { + return user?.username ? user.username : ''; + } + + private _filter(value: string|IUserDropdown): IUserDropdown[] { + const filterValue = typeof value === 'string' ? value.toLowerCase() : value.username.toLowerCase(); + + return this.options.filter(option => option.username.toLowerCase().includes(filterValue)); + } +} diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-admin-panel/tv-admin-panel.component.html b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-admin-panel/tv-admin-panel.component.html deleted file mode 100644 index 473f0e97b..000000000 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-admin-panel/tv-admin-panel.component.html +++ /dev/null @@ -1,3 +0,0 @@ -
- -
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-admin-panel/tv-admin-panel.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-admin-panel/tv-admin-panel.component.ts deleted file mode 100644 index 40ca64e1c..000000000 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-admin-panel/tv-admin-panel.component.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Component, Input, OnInit, EventEmitter, Output } from "@angular/core"; -import { RadarrService, SonarrService } from "../../../../../services"; -import { IRadarrProfile, IRadarrRootFolder, IAdvancedData, ITvRequests, ISonarrProfile, ISonarrRootFolder } from "../../../../../interfaces"; -import { MatDialog } from "@angular/material/dialog"; - -import { RequestServiceV2 } from "../../../../../services/requestV2.service"; -import { MovieAdvancedOptionsComponent } from "../../../movie/panels/movie-advanced-options/movie-advanced-options.component"; - -@Component({ - templateUrl: "./tv-admin-panel.component.html", - selector: "tv-admin-panel", -}) -export class TvAdminPanelComponent implements OnInit { - - @Input() public tv: ITvRequests; - @Output() public advancedOptionsChanged = new EventEmitter(); - @Output() public sonarrEnabledChange = new EventEmitter(); - - public sonarrEnabled: boolean; - public radarrProfiles: IRadarrProfile[]; - public selectedRadarrProfile: IRadarrProfile; - public radarrRootFolders: IRadarrRootFolder[]; - public selectRadarrRootFolders: IRadarrRootFolder; - - - public sonarrProfiles: ISonarrProfile[]; - public sonarrRootFolders: ISonarrRootFolder[]; - - constructor(private sonarrService: SonarrService, private requestService: RequestServiceV2, private dialog: MatDialog) { } - - public async ngOnInit() { - this.sonarrEnabled = await this.sonarrService.isEnabled(); - if (this.sonarrEnabled) { - this.sonarrService.getQualityProfilesWithoutSettings() - .subscribe(x => { - this.sonarrProfiles = x; - this.setQualityOverrides(); - }); - this.sonarrService.getRootFoldersWithoutSettings() - .subscribe(x => { - this.sonarrRootFolders = x; - this.setRootFolderOverrides(); - }); - } - - this.sonarrEnabledChange.emit(this.sonarrEnabled); - } - - public async openAdvancedOptions() { - const dialog = this.dialog.open(MovieAdvancedOptionsComponent, { width: "700px", data: { profiles: this.sonarrProfiles, rootFolders: this.sonarrRootFolders }, panelClass: 'modal-panel' }) - await dialog.afterClosed().subscribe(async result => { - if (result) { - // get the name and ids - result.rootFolder = result.rootFolders.filter(f => f.id === +result.rootFolderId)[0]; - result.profile = result.profiles.filter(f => f.id === +result.profileId)[0]; - await this.requestService.updateTvAdvancedOptions({ qualityOverride: result.profileId, rootPathOverride: result.rootFolderId, requestId: this.tv.id }).toPromise(); - this.advancedOptionsChanged.emit(result); - } - }); - } - - private setQualityOverrides(): void { - if (this.sonarrProfiles) { - const profile = this.sonarrProfiles.filter((p) => { - return p.id === this.tv.qualityOverride; - }); - if (profile.length > 0) { - this.tv.qualityOverrideTitle = profile[0].name; - } - } - } - - private setRootFolderOverrides(): void { - if (this.sonarrRootFolders) { - const path = this.sonarrRootFolders.filter((folder) => { - return folder.id === this.tv.rootFolder; - }); - if (path.length > 0) { - this.tv.rootPathOverrideTitle = path[0].path; - } - } - } -} diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-advanced-options/tv-advanced-options.component.html b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-advanced-options/tv-advanced-options.component.html new file mode 100644 index 000000000..5724a35bf --- /dev/null +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-advanced-options/tv-advanced-options.component.html @@ -0,0 +1,23 @@ +

+ + Advanced Options

+
+ + {{'MediaDetails.QualityProfilesSelect' | translate }} + + {{profile.name}} + + +
+
+ + {{'MediaDetails.RootFolderSelect' | translate }} + + {{profile.path}} + + +
+
+ + +
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-advanced-options/tv-advanced-options.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-advanced-options/tv-advanced-options.component.ts new file mode 100644 index 000000000..7846b69df --- /dev/null +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-advanced-options/tv-advanced-options.component.ts @@ -0,0 +1,55 @@ +import { Component, Inject, OnInit } from "@angular/core"; +import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; +import { IAdvancedData, ISonarrProfile, ISonarrRootFolder } from "../../../../../interfaces"; +import { SonarrService } from "../../../../../services"; + +@Component({ + templateUrl: "./tv-advanced-options.component.html", + selector: "tv-advanced-options", +}) +export class TvAdvancedOptionsComponent implements OnInit { + + public sonarrProfiles: ISonarrProfile[]; + public sonarrRootFolders: ISonarrRootFolder[]; + + constructor(public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: IAdvancedData, + private sonarrService: SonarrService + ) { + } + + + public async ngOnInit() { + this.sonarrService.getQualityProfilesWithoutSettings().subscribe(c => { + this.sonarrProfiles = c; + this.data.profiles = c; + this.setQualityOverrides(); + }); + this.sonarrService.getRootFoldersWithoutSettings().subscribe(c => { + this.sonarrRootFolders = c; + this.data.rootFolders = c; + this.setRootFolderOverrides(); + }); + } + + private setQualityOverrides(): void { + if (this.sonarrProfiles) { + const profile = this.sonarrProfiles.filter((p) => { + return p.id === this.data.tvRequest.qualityOverride; + }); + if (profile.length > 0) { + this.data.movieRequest.qualityOverrideTitle = profile[0].name; + } + } + } + + private setRootFolderOverrides(): void { + if (this.sonarrRootFolders) { + const path = this.sonarrRootFolders.filter((folder) => { + return folder.id === this.data.tvRequest.rootFolder; + }); + if (path.length > 0) { + this.data.movieRequest.rootPathOverrideTitle = path[0].path; + } + } + } +} diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.html b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.html index fb3c1c65e..d9d2b3a83 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.html @@ -11,11 +11,11 @@
-
+
{{'MediaDetails.RootFolderOverride' | translate }}
{{request.rootPathOverrideTitle}}
-
+
{{'MediaDetails.QualityOverride' | translate }}
{{request.qualityOverrideTitle}}
diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.html b/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.html index 0df2ba1fb..211c4a374 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.html @@ -2,127 +2,145 @@
-
-
-

{{ 'MediaDetails.NotEnoughInfo' | translate }}

+
+
+

+

{{ 'MediaDetails.NotEnoughInfo' | translate }}

+
-
- + -
+
- + -
-
-
- - - - -
- - - - -
- -
- - - - - - - - -
-
- -
-
- - - - - - - - - - - - - -
- - -
+
+
-
- - - {{tv.overview}} + + + + +
+ + + + +
+ +
+ + + + + + + + +
+ +
+ + + + + +
+
+ +
+
+ + + -
-
- +
+ +
+
+
+ + + {{tv.overview}} + + +
+
+ +
+ +
+
-
-
-
-
+
+
- + -
+
+ + +
+
+ +
+ + + + + Requests + + + + + + + +
-
-
-
- - - - - Requests - - - - - +
-
+
+
+
- - - - -
-
-
- -
- +
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.ts index ef86fbdce..8ed41e078 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.ts @@ -5,10 +5,13 @@ import { DomSanitizer } from "@angular/platform-browser"; import { ISearchTvResultV2 } from "../../../interfaces/ISearchTvResultV2"; import { MatDialog } from "@angular/material/dialog"; import { YoutubeTrailerComponent } from "../shared/youtube-trailer.component"; -import { EpisodeRequestComponent } from "../../../shared/episode-request/episode-request.component"; -import { IAdvancedData, IChildRequests, ISonarrProfile, ISonarrRootFolder, ITvRequests, RequestType } from "../../../interfaces"; +import { EpisodeRequestComponent, EpisodeRequestData } from "../../../shared/episode-request/episode-request.component"; +import { IAdvancedData, IChildRequests, ITvRequests, RequestType } from "../../../interfaces"; import { AuthService } from "../../../auth/auth.service"; import { NewIssueComponent } from "../shared/new-issue/new-issue.component"; +import { TvAdvancedOptionsComponent } from "./panels/tv-advanced-options/tv-advanced-options.component"; +import { RequestServiceV2 } from "../../../services/requestV2.service"; +import { RequestBehalfComponent } from "../shared/request-behalf/request-behalf.component"; @Component({ templateUrl: "./tv-details.component.html", @@ -30,6 +33,7 @@ export class TvDetailsComponent implements OnInit { constructor(private searchService: SearchV2Service, private route: ActivatedRoute, private sanitizer: DomSanitizer, private imageService: ImageService, public dialog: MatDialog, public messageService: MessageService, private requestService: RequestService, + private requestService2: RequestServiceV2, private auth: AuthService, private sonarrService: SonarrService) { this.route.params.subscribe((params: any) => { this.tvdbId = params.tvdbId; @@ -44,6 +48,11 @@ export class TvDetailsComponent implements OnInit { public async load() { this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); + + if (this.isAdmin) { + this.showAdvanced = await this.sonarrService.isEnabled(); + } + if (this.fromSearch) { this.tv = await this.searchService.getTvInfoWithMovieDbId(this.tvdbId); this.tvdbId = this.tv.id; @@ -60,8 +69,8 @@ export class TvDetailsComponent implements OnInit { this.tv.background = this.sanitizer.bypassSecurityTrustStyle("url(" + tvBanner + ")"); } - public async request() { - this.dialog.open(EpisodeRequestComponent, { width: "800px", data: this.tv, panelClass: 'modal-panel' }) + public async request(userId: string) { + this.dialog.open(EpisodeRequestComponent, { width: "800px", data: { series: this.tv, requestOnBehalf: userId }, panelClass: 'modal-panel' }) } public async issue() { @@ -81,6 +90,28 @@ export class TvDetailsComponent implements OnInit { }); } + public async openAdvancedOptions() { + const dialog = this.dialog.open(TvAdvancedOptionsComponent, { width: "700px", data: { tvRequest: this.showRequest }, panelClass: 'modal-panel' }) + await dialog.afterClosed().subscribe(async result => { + if (result) { + // get the name and ids + result.rootFolder = result.rootFolders.filter(f => f.id === +result.rootFolderId)[0]; + result.profile = result.profiles.filter(f => f.id === +result.profileId)[0]; + await this.requestService2.updateTvAdvancedOptions({ qualityOverride: result.profileId, rootPathOverride: result.rootFolderId, requestId: this.tv.id }).toPromise(); + this.setAdvancedOptions(result); + } + }); + } + + public async openRequestOnBehalf() { + const dialog = this.dialog.open(RequestBehalfComponent, { width: "700px", panelClass: 'modal-panel' }) + await dialog.afterClosed().subscribe(async result => { + if (result) { + await this.request(result.id); + } + }); + } + public setAdvancedOptions(data: IAdvancedData) { this.advancedOptions = data; console.log(this.advancedOptions); diff --git a/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss b/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss index f3c655c6f..c9fc9f522 100644 --- a/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss +++ b/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss @@ -222,4 +222,8 @@ .no-info { text-align: center; padding-top: 15%; +} + +.content-end { + text-align: end; } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/media-details/media-details.module.ts b/src/Ombi/ClientApp/src/app/media-details/media-details.module.ts index 2544a4935..67c0436ad 100644 --- a/src/Ombi/ClientApp/src/app/media-details/media-details.module.ts +++ b/src/Ombi/ClientApp/src/app/media-details/media-details.module.ts @@ -1,8 +1,6 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; -import { SearchService, RequestService, RadarrService } from "../services"; - import {CarouselModule} from 'primeng/carousel'; import { SharedModule } from "../shared/shared.module"; @@ -13,6 +11,7 @@ import { PipeModule } from "../pipes/pipe.module"; import * as fromComponents from './components'; import { AuthGuard } from "../auth/auth.guard"; import { ArtistDetailsComponent } from "./components/artist/artist-details.component"; +import { ReactiveFormsModule } from "@angular/forms"; const routes: Routes = [ @@ -25,6 +24,7 @@ const routes: Routes = [ imports: [ RouterModule.forChild(routes), SharedModule, + ReactiveFormsModule, PipeModule, CarouselModule, ], diff --git a/src/Ombi/ClientApp/src/app/services/identity.service.ts b/src/Ombi/ClientApp/src/app/services/identity.service.ts index 9d2b4f8c6..f25a21b73 100644 --- a/src/Ombi/ClientApp/src/app/services/identity.service.ts +++ b/src/Ombi/ClientApp/src/app/services/identity.service.ts @@ -4,7 +4,7 @@ import { Injectable, Inject } from "@angular/core"; import { HttpClient } from "@angular/common/http"; import { Observable } from "rxjs"; -import { ICheckbox, ICreateWizardUser, IIdentityResult, INotificationPreferences, IResetPasswordToken, IUpdateLocalUser, IUser, IWizardUserResult } from "../interfaces"; +import { ICheckbox, ICreateWizardUser, IIdentityResult, INotificationPreferences, IResetPasswordToken, IUpdateLocalUser, IUser, IUserDropdown, IWizardUserResult } from "../interfaces"; import { ServiceHelpers } from "./service.helpers"; @Injectable() @@ -32,6 +32,10 @@ export class IdentityService extends ServiceHelpers { return this.http.get(`${this.url}Users`, {headers: this.headers}); } + public getUsersDropdown(): Observable { + return this.http.get(`${this.url}dropdown/Users`, {headers: this.headers}); + } + public getAllAvailableClaims(): Observable { return this.http.get(`${this.url}Claims`, {headers: this.headers}); } diff --git a/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.html b/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.html index f57f014c9..099ff99e8 100644 --- a/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.html +++ b/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.html @@ -16,7 +16,7 @@
-
+
@@ -25,7 +25,7 @@ Season {{season.seasonNumber}} - Description + diff --git a/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.ts b/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.ts index 2f311b172..4e9015051 100644 --- a/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.ts +++ b/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.ts @@ -4,8 +4,12 @@ import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; import { ISearchTvResultV2 } from "../../interfaces/ISearchTvResultV2"; import { RequestService, MessageService } from "../../services"; import { ITvRequestViewModel, ISeasonsViewModel, IEpisodesRequests, INewSeasonRequests } from "../../interfaces"; +import { ThousandShortPipe } from "../../pipes/ThousandShortPipe"; - +export interface EpisodeRequestData { + series: ISearchTvResultV2; + requestOnBehalf: string | undefined; +} @Component({ selector: "episode-request", templateUrl: "episode-request.component.html", @@ -14,7 +18,7 @@ export class EpisodeRequestComponent implements OnInit { public loading: boolean; - constructor(public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public series: ISearchTvResultV2, + constructor(public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: EpisodeRequestData, private requestService: RequestService, private notificationService: MessageService) { } public ngOnInit() { @@ -25,24 +29,27 @@ export class EpisodeRequestComponent implements OnInit { public async submitRequests() { // Make sure something has been selected - const selected = this.series.seasonRequests.some((season) => { + const selected = this.data.series.seasonRequests.some((season) => { return season.episodes.some((ep) => { return ep.selected; }); }); debugger; - if (!selected && !this.series.requestAll && !this.series.firstSeason && !this.series.latestSeason) { + if (!selected && !this.data.series.requestAll && !this.data.series.firstSeason && !this.data.series.latestSeason) { this.notificationService.send("You need to select some episodes!", "OK"); return; } - this.series.requested = true; + this.data.series.requested = true; - const viewModel = { firstSeason: this.series.firstSeason, latestSeason: this.series.latestSeason, requestAll: this.series.requestAll, tvDbId: this.series.id }; + const viewModel = { + firstSeason: this.data.series.firstSeason, latestSeason: this.data.series.latestSeason, requestAll: this.data.series.requestAll, tvDbId: this.data.series.id, + requestOnBehalf: this.data.requestOnBehalf + }; viewModel.seasons = []; - this.series.seasonRequests.forEach((season) => { + this.data.series.seasonRequests.forEach((season) => { const seasonsViewModel = { seasonNumber: season.seasonNumber, episodes: [] }; - if (!this.series.latestSeason && !this.series.requestAll && !this.series.firstSeason) { + if (!this.data.series.latestSeason && !this.data.series.requestAll && !this.data.series.firstSeason) { season.episodes.forEach(ep => { if (ep.selected) { ep.requested = true; @@ -57,9 +64,9 @@ export class EpisodeRequestComponent implements OnInit { if (requestResult.result) { this.notificationService.send( - `Request for ${this.series.title} has been added successfully`); + `Request for ${this.data.series.title} has been added successfully`); - this.series.seasonRequests.forEach((season) => { + this.data.series.seasonRequests.forEach((season) => { season.episodes.forEach((ep) => { ep.selected = false; }); @@ -90,17 +97,17 @@ export class EpisodeRequestComponent implements OnInit { } public async requestAllSeasons() { - this.series.requestAll = true; + this.data.series.requestAll = true; await this.submitRequests(); } public async requestFirstSeason() { - this.series.firstSeason = true; + this.data.series.firstSeason = true; await this.submitRequests(); } public async requestLatestSeason() { - this.series.latestSeason = true; + this.data.series.latestSeason = true; await this.submitRequests(); } } diff --git a/src/Ombi/ClientApp/src/styles/shared.scss b/src/Ombi/ClientApp/src/styles/shared.scss index c743d06a9..27bfa6c7d 100644 --- a/src/Ombi/ClientApp/src/styles/shared.scss +++ b/src/Ombi/ClientApp/src/styles/shared.scss @@ -128,9 +128,9 @@ table { border: 1px solid rgba(0, 0, 0, 0.18); } -::ng-deep .mat-toolbar.mat-primary { - margin-bottom: 0.5em; -} +// ::ng-deep .mat-toolbar.mat-primary { +// margin-bottom: 0.5em; +// } ::ng-deep .dark .mat-form-field.mat-focused .mat-form-field-label { color: $accent-dark; diff --git a/src/Ombi/Controllers/V1/IdentityController.cs b/src/Ombi/Controllers/V1/IdentityController.cs index b348c1d06..2300c0482 100644 --- a/src/Ombi/Controllers/V1/IdentityController.cs +++ b/src/Ombi/Controllers/V1/IdentityController.cs @@ -275,6 +275,31 @@ namespace Ombi.Controllers.V1 return model.OrderBy(x => x.UserName); } + /// + /// Gets all users for dropdown purposes. + /// + /// Basic Information about all users + [HttpGet("dropdown/Users")] + [PowerUser] + public async Task> GetAllUsersDropdown() + { + var users = await UserManager.Users.Where(x => x.UserType != UserType.SystemUser) + .ToListAsync(); + + var model = new List(); + + foreach (var user in users) + { + model.Add(new UserViewModelDropdown + { + Id = user.Id, + Username = user.UserName + }); + } + + return model.OrderBy(x => x.Username); + } + /// /// Gets the current logged in user. /// diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index 665811489..0c133c2ee 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -271,7 +271,11 @@ "AllSeasonsTooltip": "This will request every season for this show", "FirstSeasonTooltip": "This will only request the First Season for this show", "LatestSeasonTooltip": "This will only request the Latest Season for this show" - } + }, + "SonarrConfiguration": "Sonarr Configuration", + "RadarrConfiguration": "Radarr Configuration", + "RequestOnBehalf": "Request on behalf of", + "PleaseSelectUser": "Please select a user" }, "Discovery": { "PopularTab": "Popular", From cb127f3858cab357e48efc07969c1daf71af1dca Mon Sep 17 00:00:00 2001 From: tidusjar Date: Mon, 4 Jan 2021 22:51:42 +0000 Subject: [PATCH 62/82] Added Rotten Tomatoes ratings to the detail pages --- .../IRottenTomatoesApi.cs | 14 +++ .../Models/RottenTomatoesMovieResponse.cs | 35 ++++++++ .../Models/RottenTomatoesTvResponse.cs | 20 +++++ .../Models/TvRatings.cs | 8 ++ .../Ombi.Api.RottenTomatoes.csproj | 12 +++ .../RottenTomatoesApi.cs | 56 ++++++++++++ src/Ombi.DependencyInjection/IocExtensions.cs | 2 + .../Ombi.DependencyInjection.csproj | 1 + src/Ombi.sln | 8 ++ .../ClientApp/src/app/interfaces/IRatings.ts | 11 +++ .../movie/movie-details.component.ts | 11 ++- .../movie-information-panel.component.html | 82 +++++++++--------- .../movie-information-panel.component.ts | 20 ++++- .../social-icons/social-icons.component.html | 4 +- .../tv-information-panel.component.html | 49 +++++------ .../tv-information-panel.component.ts | 8 ++ .../media-details.component.scss | 4 + .../src/app/services/searchV2.service.ts | 10 +++ src/Ombi/Controllers/V2/SearchController.cs | 23 ++++- .../wwwroot/images/rotten-audience-fresh.svg | 1 + .../wwwroot/images/rotten-audience-rotten.svg | 8 ++ src/Ombi/wwwroot/images/rotten-fresh.svg | 1 + src/Ombi/wwwroot/images/rotten-rotten.svg | 1 + src/Ombi/wwwroot/images/tmdb-logo.svg | 1 + src/Ombi/wwwroot/images/tvm-logo.png | Bin 0 -> 9354 bytes src/Ombi/wwwroot/translations/bg.json | 1 - src/Ombi/wwwroot/translations/da.json | 1 - src/Ombi/wwwroot/translations/de.json | 1 - src/Ombi/wwwroot/translations/en.json | 3 +- src/Ombi/wwwroot/translations/es.json | 1 - src/Ombi/wwwroot/translations/fr.json | 1 - src/Ombi/wwwroot/translations/hu.json | 1 - src/Ombi/wwwroot/translations/it.json | 1 - src/Ombi/wwwroot/translations/nl.json | 1 - src/Ombi/wwwroot/translations/no.json | 1 - src/Ombi/wwwroot/translations/pl.json | 1 - src/Ombi/wwwroot/translations/pt.json | 1 - src/Ombi/wwwroot/translations/ru.json | 1 - src/Ombi/wwwroot/translations/sk.json | 1 - src/Ombi/wwwroot/translations/sv.json | 1 - 40 files changed, 312 insertions(+), 95 deletions(-) create mode 100644 src/Ombi.Api.RottenTomatoes/IRottenTomatoesApi.cs create mode 100644 src/Ombi.Api.RottenTomatoes/Models/RottenTomatoesMovieResponse.cs create mode 100644 src/Ombi.Api.RottenTomatoes/Models/RottenTomatoesTvResponse.cs create mode 100644 src/Ombi.Api.RottenTomatoes/Models/TvRatings.cs create mode 100644 src/Ombi.Api.RottenTomatoes/Ombi.Api.RottenTomatoes.csproj create mode 100644 src/Ombi.Api.RottenTomatoes/RottenTomatoesApi.cs create mode 100644 src/Ombi/ClientApp/src/app/interfaces/IRatings.ts create mode 100644 src/Ombi/wwwroot/images/rotten-audience-fresh.svg create mode 100644 src/Ombi/wwwroot/images/rotten-audience-rotten.svg create mode 100644 src/Ombi/wwwroot/images/rotten-fresh.svg create mode 100644 src/Ombi/wwwroot/images/rotten-rotten.svg create mode 100644 src/Ombi/wwwroot/images/tmdb-logo.svg create mode 100644 src/Ombi/wwwroot/images/tvm-logo.png diff --git a/src/Ombi.Api.RottenTomatoes/IRottenTomatoesApi.cs b/src/Ombi.Api.RottenTomatoes/IRottenTomatoesApi.cs new file mode 100644 index 000000000..4466832b1 --- /dev/null +++ b/src/Ombi.Api.RottenTomatoes/IRottenTomatoesApi.cs @@ -0,0 +1,14 @@ +using Ombi.Api.RottenTomatoes.Models; +using System; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Ombi.Api.RottenTomatoes +{ + public interface IRottenTomatoesApi + { + Task GetMovieRatings(string movieName, int movieYear); + Task GetTvRatings(string showName, int showYear); + } +} diff --git a/src/Ombi.Api.RottenTomatoes/Models/RottenTomatoesMovieResponse.cs b/src/Ombi.Api.RottenTomatoes/Models/RottenTomatoesMovieResponse.cs new file mode 100644 index 000000000..ceea5000b --- /dev/null +++ b/src/Ombi.Api.RottenTomatoes/Models/RottenTomatoesMovieResponse.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; + +namespace Ombi.Api.RottenTomatoes.Models +{ + public class RottenTomatoesMovieResponse + { + public int total { get; set; } + public List movies { get; set; } + } + + public class Movie + { + public string id { get; set; } + public string title { get; set; } + public int year { get; set; } + public string mpaa_rating { get; set; } + public object runtime { get; set; } + public string critics_consensus { get; set; } + public MovieRatings ratings { get; set; } + public Links links { get; set; } + } + + public class MovieRatings + { + public string critics_rating { get; set; } + public int critics_score { get; set; } + public string audience_rating { get; set; } + public int audience_score { get; set; } + } + + public class Links + { + public string alternate { get; set; } + } +} diff --git a/src/Ombi.Api.RottenTomatoes/Models/RottenTomatoesTvResponse.cs b/src/Ombi.Api.RottenTomatoes/Models/RottenTomatoesTvResponse.cs new file mode 100644 index 000000000..234e958be --- /dev/null +++ b/src/Ombi.Api.RottenTomatoes/Models/RottenTomatoesTvResponse.cs @@ -0,0 +1,20 @@ +namespace Ombi.Api.RottenTomatoes.Models +{ + public class RottenTomatoesTvResponse + { + public int tvCount { get; set; } + public TvSeries[] tvSeries { get; set; } + } + + public class TvSeries + { + public string title { get; set; } + public int startYear { get; set; } + public int endYear { get; set; } + public string url { get; set; } + public string meterClass { get; set; } + public int meterScore { get; set; } + public string image { get; set; } + } + +} diff --git a/src/Ombi.Api.RottenTomatoes/Models/TvRatings.cs b/src/Ombi.Api.RottenTomatoes/Models/TvRatings.cs new file mode 100644 index 000000000..902d532d7 --- /dev/null +++ b/src/Ombi.Api.RottenTomatoes/Models/TvRatings.cs @@ -0,0 +1,8 @@ +namespace Ombi.Api.RottenTomatoes.Models +{ + public class TvRatings + { + public string Class { get; set; } + public int Score { get; set; } + } +} diff --git a/src/Ombi.Api.RottenTomatoes/Ombi.Api.RottenTomatoes.csproj b/src/Ombi.Api.RottenTomatoes/Ombi.Api.RottenTomatoes.csproj new file mode 100644 index 000000000..8cb4799bc --- /dev/null +++ b/src/Ombi.Api.RottenTomatoes/Ombi.Api.RottenTomatoes.csproj @@ -0,0 +1,12 @@ + + + + net5.0 + 8.0 + + + + + + + diff --git a/src/Ombi.Api.RottenTomatoes/RottenTomatoesApi.cs b/src/Ombi.Api.RottenTomatoes/RottenTomatoesApi.cs new file mode 100644 index 000000000..88dfa2f79 --- /dev/null +++ b/src/Ombi.Api.RottenTomatoes/RottenTomatoesApi.cs @@ -0,0 +1,56 @@ +using Ombi.Api.RottenTomatoes.Models; +using System; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Ombi.Api.RottenTomatoes +{ + public class RottenTomatoesApi : IRottenTomatoesApi + { + public RottenTomatoesApi(IApi api) + { + _api = api; + } + + private string Endpoint => "https://www.rottentomatoes.com/api/private"; + private IApi _api { get; } + + public async Task GetMovieRatings(string movieName, int movieYear) + { + var request = new Request("/v1.0/movies", Endpoint, HttpMethod.Get); + request.AddHeader("Accept", "application/json"); + request.AddQueryString("q", movieName); + var result = await _api.Request(request); + + var movieFound = result.movies.FirstOrDefault(x => x.year == movieYear); + if (movieFound == null) + { + return null; + } + + return movieFound.ratings; + } + + public async Task GetTvRatings(string showName, int showYear) + { + var request = new Request("/v2.0/search/", Endpoint, HttpMethod.Get); + request.AddHeader("Accept", "application/json"); + request.AddQueryString("q", showName); + request.AddQueryString("limit", 10.ToString()); + var result = await _api.Request(request); + + var showFound = result.tvSeries.FirstOrDefault(x => x.startYear == showYear); + if (showFound == null) + { + return null; + } + + return new TvRatings + { + Class = showFound.meterClass, + Score = showFound.meterScore + }; + } + } +} diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index 7571cdca4..d5e577d84 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -67,6 +67,7 @@ using Quartz.Spi; using Ombi.Api.MusicBrainz; using Ombi.Api.Twilio; using Ombi.Api.CloudService; +using Ombi.Api.RottenTomatoes; namespace Ombi.DependencyInjection { @@ -158,6 +159,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); } public static void RegisterStore(this IServiceCollection services) { diff --git a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index bbfe532eb..ed1e9e4a2 100644 --- a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -32,6 +32,7 @@ + diff --git a/src/Ombi.sln b/src/Ombi.sln index 658d37c3b..70324f967 100644 --- a/src/Ombi.sln +++ b/src/Ombi.sln @@ -121,6 +121,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Webhook", "Ombi.Ap EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.CloudService", "Ombi.Api.CloudService\Ombi.Api.CloudService.csproj", "{5DE40A66-B369-469E-8626-ECE23D9D8034}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.RottenTomatoes", "Ombi.Api.RottenTomatoes\Ombi.Api.RottenTomatoes.csproj", "{8F19C701-7881-4BC7-8BBA-B068A6B954AD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -323,6 +325,10 @@ Global {5DE40A66-B369-469E-8626-ECE23D9D8034}.Debug|Any CPU.Build.0 = Debug|Any CPU {5DE40A66-B369-469E-8626-ECE23D9D8034}.Release|Any CPU.ActiveCfg = Release|Any CPU {5DE40A66-B369-469E-8626-ECE23D9D8034}.Release|Any CPU.Build.0 = Release|Any CPU + {8F19C701-7881-4BC7-8BBA-B068A6B954AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F19C701-7881-4BC7-8BBA-B068A6B954AD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F19C701-7881-4BC7-8BBA-B068A6B954AD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F19C701-7881-4BC7-8BBA-B068A6B954AD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -334,6 +340,7 @@ Global {63E63511-1C7F-4162-8F92-8F7391B3C8A3} = {025FB189-2FFB-4F43-A64B-6F1B5A0D2065} {2E1A7B91-F29B-42BC-8F1E-1CF2DCC389BA} = {9293CA11-360A-4C20-A674-B9E794431BF5} {08FF107D-31E1-470D-AF86-E09B015CEE06} = {9293CA11-360A-4C20-A674-B9E794431BF5} + {F03757C7-5145-45C9-AFFF-B4E946755779} = {9293CA11-360A-4C20-A674-B9E794431BF5} {CFB5E008-D0D0-43C0-AA06-89E49D17F384} = {9293CA11-360A-4C20-A674-B9E794431BF5} {0E8EF835-E4F0-4EE5-A2B6-678DEE973721} = {9293CA11-360A-4C20-A674-B9E794431BF5} {E6EE2830-E4AC-4F2E-AD93-2C9305605761} = {EA30DD15-6280-4687-B370-2956EC2E54E5} @@ -369,6 +376,7 @@ Global {59D19538-0496-44EE-936E-EBBC22CF7B27} = {410F36CF-9C60-428A-B191-6FD90610991A} {E2186FDA-D827-4781-8663-130AC382F12C} = {9293CA11-360A-4C20-A674-B9E794431BF5} {5DE40A66-B369-469E-8626-ECE23D9D8034} = {9293CA11-360A-4C20-A674-B9E794431BF5} + {8F19C701-7881-4BC7-8BBA-B068A6B954AD} = {9293CA11-360A-4C20-A674-B9E794431BF5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {192E9BF8-00B4-45E4-BCCC-4C215725C869} diff --git a/src/Ombi/ClientApp/src/app/interfaces/IRatings.ts b/src/Ombi/ClientApp/src/app/interfaces/IRatings.ts new file mode 100644 index 000000000..fe7a86614 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/interfaces/IRatings.ts @@ -0,0 +1,11 @@ +export interface IMovieRatings { + critics_rating: string; + critics_score: number; + audience_rating: string; + audience_score: number; +} + +export interface ITvRatings { + class: string; + score: number; +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts index 56b9d3ffe..7ffb79eee 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts @@ -1,4 +1,4 @@ -import { Component, ViewEncapsulation } from "@angular/core"; +import { Component, Inject, OnInit, ViewEncapsulation } from "@angular/core"; import { ImageService, SearchV2Service, RequestService, MessageService, RadarrService } from "../../../services"; import { ActivatedRoute } from "@angular/router"; import { DomSanitizer } from "@angular/platform-browser"; @@ -13,13 +13,15 @@ import { StorageService } from "../../../shared/storage/storage-service"; import { MovieAdvancedOptionsComponent } from "./panels/movie-advanced-options/movie-advanced-options.component"; import { RequestServiceV2 } from "../../../services/requestV2.service"; import { RequestBehalfComponent } from "../shared/request-behalf/request-behalf.component"; +import { IMovieRatings } from "../../../interfaces/IRatings"; +import { APP_BASE_HREF } from "@angular/common"; @Component({ templateUrl: "./movie-details.component.html", styleUrls: ["../../media-details.component.scss"], encapsulation: ViewEncapsulation.None }) -export class MovieDetailsComponent { +export class MovieDetailsComponent implements OnInit { public movie: ISearchMovieResultV2; public hasRequest: boolean; public movieRequest: IMovieRequests; @@ -43,10 +45,13 @@ export class MovieDetailsComponent { } } this.theMovidDbId = params.movieDbId; - this.load(); }); } + public async ngOnInit() { + await this.load(); + } + public async load() { this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html index 3f838d9ee..62a458c19 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html @@ -1,16 +1,26 @@
+ + {{movie.voteAverage | number:'1.0-1'}}/10 + + + {{ratings.critics_score}}% + + + {{ratings.audience_score}}% + +
{{'MediaDetails.Status' | translate }}: -
{{movie.status}}
+ {{movie.status}}
- {{'MediaDetails.Availability' | translate }} -
{{'Common.Available' | translate}}
-
{{'Common.NotAvailable' | translate}}
+ {{'MediaDetails.Availability' | translate }}: + {{'Common.Available' | translate}} + {{'Common.NotAvailable' | translate}}
-
-
+ +
{{'MediaDetails.RequestStatus' | translate }}
{{'Common.ProcessingRequest' | translate}}
{{'Common.PendingApproval' | translate}} @@ -20,13 +30,13 @@
- {{'Requests.RequestedBy' | translate }} -
{{request.requestedUser.userAlias}}
+ {{'Requests.RequestedBy' | translate }}: + {{request.requestedUser.userAlias}}
- {{'Requests.RequestDate' | translate }} -
{{request.requestedDate | date}}
+ {{'Requests.RequestDate' | translate }}: + {{request.requestedDate | date}}
@@ -43,56 +53,49 @@ {{'MediaDetails.QualityOverride' | translate }}
{{request.qualityOverrideTitle}}
-
-
- {{'MediaDetails.Genres' | translate }}: -
- - - {{genre.name}} - - -
-
-
+ + + +
{{'MediaDetails.TheatricalRelease' | translate }}: -
- {{movie.releaseDate | date: 'mediumDate'}} +
{{'MediaDetails.DigitalRelease' | translate }}: -
{{movie.digitalReleaseDate | date: 'mediumDate'}} -
-
-
- {{'MediaDetails.UserScore' | translate }}: -
- {{movie.voteAverage | number:'1.0-1'}} / 10 -
+
{{'MediaDetails.Votes' | translate }}: -
{{movie.voteCount | thousandShort: 1}} -
{{'MediaDetails.Runtime' | translate }}: -
{{'MediaDetails.Minutes' | translate:{runtime: movie.runtime} }}
+ {{'MediaDetails.Minutes' | translate:{runtime: movie.runtime} }}
{{'MediaDetails.Revenue' | translate }}: -
{{movie.revenue | currency: 'USD'}}
+ {{movie.revenue | currency: 'USD'}}
{{'MediaDetails.Budget' | translate }}: -
{{movie.budget | currency: 'USD'}}
+ {{movie.budget | currency: 'USD'}}
+
+
+ {{'MediaDetails.Genres' | translate }}: +
+ + + {{genre.name}} + + +
+
-
+
{{'MediaDetails.Keywords' | translate }}: @@ -100,5 +103,4 @@ {{keyword.name}} -
-
\ No newline at end of file +
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.ts index 3bfb68f56..1df97372c 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.ts @@ -1,15 +1,27 @@ -import { Component, ViewEncapsulation, Input } from "@angular/core"; +import { Component, ViewEncapsulation, Input, OnInit, Inject } from "@angular/core"; import { ISearchMovieResultV2 } from "../../../../interfaces/ISearchMovieResultV2"; -import { IAdvancedData, IMovieRequests } from "../../../../interfaces"; - +import { IMovieRequests } from "../../../../interfaces"; +import { SearchV2Service } from "../../../../services/searchV2.service"; +import { IMovieRatings } from "../../../../interfaces/IRatings"; +import { APP_BASE_HREF } from "@angular/common"; @Component({ templateUrl: "./movie-information-panel.component.html", styleUrls: ["../../../media-details.component.scss"], selector: "movie-information-panel", encapsulation: ViewEncapsulation.None }) -export class MovieInformationPanelComponent { +export class MovieInformationPanelComponent implements OnInit { + + constructor(private searchService: SearchV2Service, @Inject(APP_BASE_HREF) public baseUrl: string) { } + @Input() public movie: ISearchMovieResultV2; @Input() public request: IMovieRequests; @Input() public advancedOptions: boolean; + + public ratings: IMovieRatings; + + public ngOnInit() { + this.searchService.getRottenMovieRatings(this.movie.title, +this.movie.releaseDate.toString().substring(0,4)) + .subscribe(x => this.ratings = x); + } } diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html index de923204a..68fff403d 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html @@ -10,10 +10,10 @@ - - diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.html b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.html index d9d2b3a83..b7d8f6872 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.html @@ -1,42 +1,46 @@
+ + {{tv.rating}}/10 + + + {{ratings.score}}% + + +
{{'MediaDetails.Status' | translate }}: -
{{tv.status}} -
First Aired: -
{{tv.firstAired | date: 'mediumDate'}} -
+
+ +
+ Seasons: + {{seasonCount}} +
+
+ Episodes: + {{totalEpisodes}}
- {{'MediaDetails.RootFolderOverride' | translate }} + {{'MediaDetails.RootFolderOverride' | translate }}:
{{request.rootPathOverrideTitle}}
- {{'MediaDetails.QualityOverride' | translate }} + {{'MediaDetails.QualityOverride' | translate }}:
{{request.qualityOverrideTitle}}
{{'MediaDetails.Runtime' | translate }}: -
{{'MediaDetails.Minutes' | translate:{ runtime: tv.runtime} }} -
-
- Rating: -
- {{tv.rating}} / 10 -
-
+
Network: -
{{tv.network.name}} -
@@ -48,16 +52,3 @@
-
-
- Seasons: -
- {{seasonCount}} -
-
-
- Episodes: -
- {{totalEpisodes}} -
-
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.ts index 3d3f77e75..5217586f5 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.ts @@ -1,6 +1,8 @@ import { Component, ViewEncapsulation, Input, OnInit } from "@angular/core"; import { ITvRequests } from "../../../../../interfaces"; +import { ITvRatings } from "../../../../../interfaces/IRatings"; import { ISearchTvResultV2 } from "../../../../../interfaces/ISearchTvResultV2"; +import { SearchV2Service } from "../../../../../services"; @Component({ templateUrl: "./tv-information-panel.component.html", @@ -9,15 +11,21 @@ import { ISearchTvResultV2 } from "../../../../../interfaces/ISearchTvResultV2"; encapsulation: ViewEncapsulation.None }) export class TvInformationPanelComponent implements OnInit { + + constructor(private searchService: SearchV2Service) { } + @Input() public tv: ISearchTvResultV2; @Input() public request: ITvRequests; @Input() public advancedOptions: boolean; + public ratings: ITvRatings; public seasonCount: number; public totalEpisodes: number = 0; public nextEpisode: any; public ngOnInit(): void { + this.searchService.getRottenTvRatings(this.tv.title, +this.tv.firstAired.toString().substring(0,4)) + .subscribe(x => this.ratings = x); this.tv.seasonRequests.forEach(season => { this.totalEpisodes = this.totalEpisodes + season.episodes.length; }); diff --git a/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss b/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss index c9fc9f522..7466e26ca 100644 --- a/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss +++ b/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss @@ -226,4 +226,8 @@ .content-end { text-align: end; +} + +.rating-small { + width: 1.3em; } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/services/searchV2.service.ts b/src/Ombi/ClientApp/src/app/services/searchV2.service.ts index 6536e3cf6..07a846185 100644 --- a/src/Ombi/ClientApp/src/app/services/searchV2.service.ts +++ b/src/Ombi/ClientApp/src/app/services/searchV2.service.ts @@ -11,6 +11,7 @@ import { ISearchMovieResultV2 } from "../interfaces/ISearchMovieResultV2"; import { ISearchTvResultV2, IMovieCollectionsViewModel, IActorCredits } from "../interfaces/ISearchTvResultV2"; import { IArtistSearchResult, IAlbumArt } from "../interfaces/IMusicSearchResultV2"; import { SearchFilter } from "../my-nav/SearchFilter"; +import { IMovieRatings, ITvRatings } from "../interfaces/IRatings"; @Injectable() export class SearchV2Service extends ServiceHelpers { @@ -121,4 +122,13 @@ export class SearchV2Service extends ServiceHelpers { public getReleaseGroupArt(mbid: string): Observable { return this.http.get(`${this.url}/releasegroupart/${mbid}`); } + + public getRottenMovieRatings(name: string, year: number): Observable { + return this.http.get(`${this.url}/ratings/movie/${name}/${year}`); + } + + public getRottenTvRatings(name: string, year: number): Observable { + return this.http.get(`${this.url}/ratings/tv/${name}/${year}`); + } + } diff --git a/src/Ombi/Controllers/V2/SearchController.cs b/src/Ombi/Controllers/V2/SearchController.cs index 652ff15eb..af1378ee5 100644 --- a/src/Ombi/Controllers/V2/SearchController.cs +++ b/src/Ombi/Controllers/V2/SearchController.cs @@ -14,13 +14,15 @@ using Ombi.Core.Models.Search; using Ombi.Core.Models.Search.V2; using Ombi.Core.Models.Search.V2.Music; using Ombi.Models; +using Ombi.Api.RottenTomatoes.Models; +using Ombi.Api.RottenTomatoes; namespace Ombi.Controllers.V2 { public class SearchController : V2Controller { public SearchController(IMultiSearchEngine multiSearchEngine, ITvSearchEngine tvSearchEngine, - IMovieEngineV2 v2Movie, ITVSearchEngineV2 v2Tv, IMusicSearchEngineV2 musicEngine) + IMovieEngineV2 v2Movie, ITVSearchEngineV2 v2Tv, IMusicSearchEngineV2 musicEngine, IRottenTomatoesApi rottenTomatoesApi) { _multiSearchEngine = multiSearchEngine; _tvSearchEngine = tvSearchEngine; @@ -29,6 +31,7 @@ namespace Ombi.Controllers.V2 _movieEngineV2.ResultLimit = 12; _tvEngineV2 = v2Tv; _musicEngine = musicEngine; + _rottenTomatoesApi = rottenTomatoesApi; } private readonly IMultiSearchEngine _multiSearchEngine; @@ -36,6 +39,7 @@ namespace Ombi.Controllers.V2 private readonly ITVSearchEngineV2 _tvEngineV2; private readonly ITvSearchEngine _tvSearchEngine; private readonly IMusicSearchEngineV2 _musicEngine; + private readonly IRottenTomatoesApi _rottenTomatoesApi; /// /// Returns search results for both TV and Movies @@ -399,5 +403,22 @@ namespace Ombi.Controllers.V2 { return await _musicEngine.GetReleaseGroupArt(musicBrainzId, CancellationToken); } + + [HttpGet("ratings/movie/{name}/{year}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesDefaultResponseType] + public Task GetRottenMovieRatings(string name, int year) + { + return _rottenTomatoesApi.GetMovieRatings(name, year); + } + + [HttpGet("ratings/tv/{name}/{year}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesDefaultResponseType] + public Task GetRottenTvRatings(string name, int year) + { + return _rottenTomatoesApi.GetTvRatings(name, year); + } + } } \ No newline at end of file diff --git a/src/Ombi/wwwroot/images/rotten-audience-fresh.svg b/src/Ombi/wwwroot/images/rotten-audience-fresh.svg new file mode 100644 index 000000000..ecc9b5b0e --- /dev/null +++ b/src/Ombi/wwwroot/images/rotten-audience-fresh.svg @@ -0,0 +1 @@ + diff --git a/src/Ombi/wwwroot/images/rotten-audience-rotten.svg b/src/Ombi/wwwroot/images/rotten-audience-rotten.svg new file mode 100644 index 000000000..c97f1f65a --- /dev/null +++ b/src/Ombi/wwwroot/images/rotten-audience-rotten.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/Ombi/wwwroot/images/rotten-fresh.svg b/src/Ombi/wwwroot/images/rotten-fresh.svg new file mode 100644 index 000000000..ff792bcf5 --- /dev/null +++ b/src/Ombi/wwwroot/images/rotten-fresh.svg @@ -0,0 +1 @@ + diff --git a/src/Ombi/wwwroot/images/rotten-rotten.svg b/src/Ombi/wwwroot/images/rotten-rotten.svg new file mode 100644 index 000000000..283ea5b60 --- /dev/null +++ b/src/Ombi/wwwroot/images/rotten-rotten.svg @@ -0,0 +1 @@ + diff --git a/src/Ombi/wwwroot/images/tmdb-logo.svg b/src/Ombi/wwwroot/images/tmdb-logo.svg new file mode 100644 index 000000000..e98e4ab29 --- /dev/null +++ b/src/Ombi/wwwroot/images/tmdb-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Ombi/wwwroot/images/tvm-logo.png b/src/Ombi/wwwroot/images/tvm-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a4912fdafca107ec9a5df3875b31f80cf875120b GIT binary patch literal 9354 zcmV;5Bz4<~P)Oz@Z0f2-7z;ux~O9+4z06=<WDR*FRcSTFz- zW=q650N5=6FiBTtNC2?60Km==3$g$R3;-}uh=nNt1bYBr$Ri_o0EC$U6h`t_Jn<{8 z5a%iY0C<_QJh>z}MS)ugEpZ1|S1ukX&Pf+56gFW3VVXcL!g-k)GJ!M?;PcD?0HBc- z5#WRK{dmp}uFlRjj{U%*%WZ25jX z{P*?XzTzZ-GF^d31o+^>%=Ap99M6&ogks$0k4OBs3;+Bb(;~!4V!2o<6ys46agIcq zjPo+3B8fthDa9qy|77CdEc*jK-!%ZRYCZvbku9iQV*~a}ClFY4z~c7+0P?$U!PF=S z1Au6Q;m>#f??3%Vpd|o+W=WE9003S@Bra6Svp>fO002awfhw>;8}z{#EWidF!3EsG z3;bXU&9EIRU@z1_9W=mEXoiz;4lcq~xDGvV5BgyU zp1~-*fe8db$Osc*A=-!mVv1NJjtCc-h4>-CNCXm#Bp}I%6j35eku^v$Qi@a{RY)E3 zJ#qp$hg?Rwkvqr$GJ^buyhkyVfwECO)C{#lxu`c9ghrwZ&}4KmnvWKso6vH!8a<3Q zq36)6Xb;+tK10Vaz~~qUGsJ8#F2=(`u{bOVlVi)VBCHIn#u~6ztOL7=^<&SmcLWlF zMZgI*1b0FpVIDz9SWH+>*hr`#93(Um+6gxa1B6k+CnA%mOSC4s5&6UzVlpv@SV$}* z))J2sFA#f(L&P^E5{W}HC%KRUNwK6<(h|}}(r!{C=`5+6G)NjFlgZj-YqAG9lq?`C z$c5yc>d>VnA`E_*3F2Qp##d8RZb=H01_mm@+|Cqnc9PsG(F5HIG_C zt)aG3uTh7n6Et<2In9F>NlT@zqLtGcXcuVrX|L#Xx)I%#9!{6gSJKPrN9dR61N3(c z4Tcqi$B1Vr8Jidf7-t!G7_XR2rWwr)$3XQ?}=hpK0&Z&W{| zep&sA23f;Q!%st`QJ}G3cbou<7-yIK2z4nfCCCtN2-XOGSWo##{8Q{ATurxr~;I`ytDs%xbip}RzP zziy}Qn4Z2~fSycmr`~zJ=lUFdFa1>gZThG6M+{g7vkW8#+YHVaJjFF}Z#*3@$J_By zLtVo_L#1JrVVB{Ak-5=4qt!-@Mh}c>#$4kh<88)m#-k<%CLtzEP3leVno>={htGUuD;o7bD)w_sX$S}eAxwzy?UvgBH(S?;#HZiQMoS*2K2 zT3xe7t(~nU*1N5{rxB;QPLocnp4Ml>u<^FZwyC!nu;thW+pe~4wtZn|Vi#w(#jeBd zlf9FDx_yoPJqHbk*$%56S{;6Kv~mM9!g3B(KJ}#RZ#@)!hR|78Dq|Iq-afF%KE1Brn_fm;Im z_u$xr8UFki1L{Ox>G0o)(&RAZ;=|I=wN2l97;cLaHH6leTB-XXa*h%dBOEvi`+x zi?=Txl?TadvyiL>SuF~-LZ;|cS}4~l2eM~nS7yJ>iOM;atDY;(?aZ^v+mJV$@1Ote z62cPUlD4IWOIIx&SmwQ~YB{nzae3Pc;}r!fhE@iwJh+OsDs9zItL;~pu715HdQEGA zUct(O!LkCy1<%NCg+}G`0PgpNm-?d@-hMgNe6^V+j6x$b<6@S<$+<4_1hi}Ti zncS4LsjI}fWY1>OX6feMEuLErma3QLmkw?X+1j)X-&VBk_4Y;EFPF_I+q;9dL%E~B zJh;4Nr^(LEJ3myURP{Rblsw%57T)g973R8o)DE9*xN#~;4_o$q%o z4K@u`jhx2fBXC4{U8Qn{*%*B$Ge=nny$HAYq{=vy|sI0 z_vss+H_qMky?OB#|JK!>IX&II^LlUh#rO5!7TtbwC;iULyV-Xq?ybB}ykGP{?LpZ? z-G|jbTmIbG@7#ZCz;~eY(cDM(28Dyq{*m>M4?_iynUBkc4TkHUI6gT!;y-fz>HMcd z&t%Ugo)`Y2{>!cx7B7DI)$7;J(U{Spm-3gBzioV_{p!H$8L!*M!p0uH$#^p{Ui4P` z?ZJ24cOCDe-w#jZd?0@)|7iKK^;6KN`;!@ylm7$*nDhK&GcDTy000JJOGiWi{{a60 z|De66lK=n!32;bRa{vGf6951U69E94oEQKA00(qQO+^RZ3IY-(4UM_nkpKW0yh%hs zRCwC$U3qjARo4H#>Lm#v;E0Smihd5WxPXI>TNoUfNB{{EB5PR2bfsmw=bat9mDl7&f`zFXHOF|Mtl1_KJ-u?bahk?$kPIp&VCqVsjigW0y zdiTD&ynEkT>`&`U2?h-4i2n>6!nBt0r}d?40Sq#vsU2p+;|*L2FzhspP7gA~Y2Z@u zdc#hQNN>Q1^ac#4H()rufk2$zNSh2?64||Br{RKRh`@jWBht6DtehLtF%S@#q<`!% zBE8;ydd5)x_YWXh%!Y&v_yuon#{IKqz~gMvmCn|;KQV+Ij9xq*-2K)I!dVYtq-W>< zF<_wGI1d655$S3%M&=o$yc7-)2{R;Nq$eV5SW{%Uy@7x*pz(!BD9pb>@*U0QlElhIBa z&>T?=8@iPAVm2CS1KRuah72wxy%-bm1~lVLS%o2lO9)1v-hgJDUb(>#!X?$GH=N#p zcB}+&PUT(~Lp&Xo-WyYP26Te#u*Vrv=!8zM^1DRdrFyAxQ{xj)21;jAi4BqhHo4UHB!HIzGy!-mbx2~dE(Tm;yA2F! zUYWeQa6WPp`Ly`N*5-Y5WbqTM;z1lWFmZ*9XAYI{;qOiH9g6axWe_Iuv=4 zF6n)8arPv&GW5EVi?hdwcv(DjG?I(62NTf{pLbqQF3!F|hxEzjL#JnQarUwR<<^ae zk_Gkq7yu>|WohQGwG1Lk7tooMT%6rK95j-PvwuQFNdg-C!YuFKMA+7@OD@jt)s7ym zMD&`DJPZiM;r*xg8b|Lr(f2!`yJGRf&v=zvvj9vC2l4SmSzy3)71=!;pxk2A##ts5 zWi_M^OX@nlC<{x6C56mm`miKSD9WngL?)HTx&WxtA`35#-}q@1dJ#|+YCU{p*0VF& znwKYw&h+m9urWk=sSM;n{1+%{)_he5vjmFJAIv7y%3BmQemrN-omP2w!uX=h?%~kF z(qTy;(91$95_1JoM%{*rUc{hQ&o1MOGIO*z%F*@b$G}iNK(j z{OD|EI2;}TnrrJ^1d5=oNvOp^BSI2hE*(|mz}OlDNCD8tz`<*F+QPo?EwLz1Lc&KRwRH@0v&#ujD{;e_74*pc<<*xLdvGxO0gNGizK&Org3R;f+*Nd+0- zP_kdW#(f${xuf%!Jw-%!wr@|`cgKtu(0xtgtAs7K<0sH$)< zvuPZ;c_{Pj4FC@Nvvp08UaR6;8Im3LZvcGL-t#x`-?vc}al+=E8&+axa5VkYc0_gN zio-oe3s;BUmuO#hGl350B#aM1ka%45yZhK_Fq3I%FiPSSu-Sex+Mbc?g!6b&!c^e{ z!|aO?gP3cHXl`JGJkY$ng$S<(@%S)r#Mf$T9Dp3k`=jhj!N6_=VB;P|lttp4VBanaA^ zjvPNDm?B5!Ed>L9E1GI!W)tf2*K&Df{_}wkvUtZ*1Fk#mi=wEWBlDIv=ZzfShBt}B ztT28s;LMR}JMreRyrs8uqCP>q@|jkr*NU8k@pvq6={hxec&cjQ+`0>pq`tDlu4vIC z19kX`LVl*~T3RLFOE0uGih9zMSLAUJjc?k21S<7=HP|xqMveyoue2?T(0u9)l)Cz| z;FSV3{zO+7&S;^p!?*Ir8GS)Ow?NC|q%F`BXqvF>^V3&t?nRHxOTRrKH@(B0U}RqU zLIQ3P>5wZN)pN4|Ntzi*#@e19JHvt9YPt{0hMQpf8NOGzTSV`m&+>EmYeAd~X(r^R zKM_osBXXDgfQb6EDNB;jxOoSd)iNge9KD6T{4UazZSkD zF>sGZT%NYZ`&m($?nB2xlBCA}Gxo{+D1< zYBfC)QHLKRrrV0VP*M)P)BD!GUu;~$Do=xRu+&w!`0XtK3xY86T#*u~u?eoNyZ4vL zD~<;1H7$KXA6YcH(X{$2#0UGIZJvH=qvz8kgnoHg=+bdv$PCR_udJqTbF+$p(Q))p4h=7sug$hHB9_nzSoKqCyi{coT0X~4}|O2%_cOhuW-r^`=_DEK^P&- z=5ru=uYN)9J6FAs+%m${7IzJ`rTr5?BPaT~S_GM*h5vCL24Ym6X=|uozr{vWI?|_pOE}1iq zzX@q79ImpjtK=1VE^@hl-L!JE8s87cXQy4Qx3fQzm5L$Rsoy8U3?Uy(g0r*?Q1}7a zVGn&HmYNaQUS=Ykc_b^<8kd!#i?2oeV;3Ztq|=j!mZiD)SgER+F7*~Rpwql5 zXB#+@rXVgWrASzh)74Opohm6?SKJty9GFlRKL3w(<*nWj{p{ml(Sd!tkz8)La#jJ1 zJtRBqq$YD9xT(^?nTsGXb}NDUw#}v_xWC*&^2+=-TZ@j3OL$RfZ2U+xfC%740RPcL z-(EfeU<*OK?UkG^xQo^UR?Bk$&S_czqpC;k<57;JDR%9?CP;oG9eGJWi}a23kXPDY zap$a7IAfx^b|0L%=;?=-FS_Wx*8g*&E^Xcc&|JO?%xZZ@%ixbc*?U)mZG0T|mPJM3S!@}~AIvB2fN>hY=IME??%qck|dzx{5nkK8|YXRJ_E`0g_} zbJ5TA(6^V*QJiRiu-+CkTuTZ8t0hBr*ym_*`1bUu_0lV=zWn?2|83oO@tSjL^#8be z%*D%FSC5&BhnFwP7xHQ{LD~7uH?qUNHuQDT{$+8&^5%!Ysoo=RQyZ7?E_h<_tgiqR z-Y7fl_g`2>d$fY!`4LeW{KzT5V*ayl14#9aRn+8Gqy77E>-d+;7h>@86blh-;(k0s zbM6|D(^V%s?Bg`0^#SNHY$`8my(PoG}YGV0vLh>c4COwv$g({`vt`~DAQEIgmK z4$4@7UuG-gEAHX#3Y4ygIaw(yAOdMI0^v$ zwQOVwRFB*wftyUhU7W)`-v)BJqV+Z6)Kk5gsa!3slXDC(EqpV z$33|u9Qqx&YnT0o2J1jg*L!#+Q}1Ji!=s3^f5L*>;cfc~<>?hcQy37kIi zk)9$R=`V4mFl|wm95&-m4Eh{FfrBdSC zzZkjfedLXMA!Io|xO5&Qpa&7HSAD3s45&Q^Fg7||X??qT2=?dxg$AIU(sMATe%Tn(%`XMQtFjeYxMa zWL^(Wbgf>~Qk-Z^s1D!4-`qDi(T{{>n%3F#w>~#o>&>lvW53?Uo((S>aCW+t*`fK&MIXsYBCy zZ{PTBw&R-u&;55I(Df-&`EJ1Fo*GVtD1^53DKavr7QYB2MSNJuy-5nJfD4_&qo~ao z%*Mr^A@hrS7Jm)`k0O#btA3ev%U-a$1|W>nbHVD7p`#<#0v-X3S&LP zt2__%>qUVaQ7lO?RqO>UW?Q)BKoMvG)nwjY`gu><=RYU3-8WAr6QY(oAf2cTiV647 z2f+K@B)U5=nEI3_KZ|=7f1XQfC;rwuVcxt8VCXM9?4C&2GihArtY22$3HtQHz9n_Y z;X)ruyAK7_@1&YKu!g32@`}7Lj;uy*^HHdab2!K*e6P_7>0}is${P3;ZH0N$PH;)M zI4Wj9+Gi7gmO3Y})IROA|K&vSoQSn4OW}x0EdUti>eY`ASdX3H5RFc9 zHHB|Ia7~Xl-M9Y_ShN3N5WQj(AhExMGfkub7w70E^LEBk1}vGwNTKUQ_pDp@fy|}{ zWrux3FnkPrXxK7T){LdAgDxd$VqPY_in$G+1NH^8v3WqS?5otO@r3lXN)juF0Vt=I>W z)Q=X5`mzF0_+ych1nJVJSEIw!9E=g@ebGCn6Q?27o_jaM@sw|^+xG9{CMM*kU&`$F z?_4B4glPMe=g==@b`gPY@cY?E?^IcsJ|X#Czm(ZhsPYZNUs=DD*}c_d5-MMv;xyb2 z>eEAZ*elFOPF8qNtPx(8gU0}gZHtT?^pSnAw1~)}c6zS0Vn4`gSriH%v2h7t45{Y* zN7eWlzhmn2{^~I>!r!L{weuKxxz1`(1C&74Cw>x<%AHJPHd*kKw~nExx9b-beOh7pBS@r|-uR%*QJmI?Cte zRRx*W1IJp_Lwu#ng@-&sz5$(S<&@n$aV}ah--?Bk{*BB0Xi-ZN+9peZ3!w*#yOGLiCn>=(@I| zLnk+wM|q{k(o&Gy)54b|p}TDj!1+4aVRz}Vu*2Q=CaIfaGpzfLK(Uy?C5Eo6$`Q5e zpcHOeQM(VZE^D`fGX}+KM%2!O=(h4JH21kqOiPled;WS@cO8OalAu^E=(=VLqIVp` z`qc%QT6l?#O8{BSpLEY%2kGRQmbw$6ThRuXPgVQ1gRbkhIitSb2gPbWZ$Elnmi}VfHwj#Q+C+@8YaFDf#?>Wy2pF>7O1_m5JXcWGVsf<-__Bd$R9N|Hp$`Dv05pVR4{E+0(e&Os&m9lOK83_u6wTO&vco=6j}$u4 zex~fOp9OFmB-VVMFCb25hX;UPfU(RAX#P%i*xwCjX5p>hzX7~_fy`3@JmgR2vcrBm z7+Z7!Z!ZJ*dl>aJ0E}(Dp!~`H9Nx>*uff>t^Y7?+F_s0OM*t2F07+o%qcGFo4d5o( zVQ&ceNH&#=^_};juu=z}`UBI^@!S2aGeB>?IRU8<4(y4kbb=-{pH z2Cg_rC!c%SWJG#>BfY;qy~VIW14f_T=+hf8a9Q-}dw=>bx>99e%+njVObycB#fB_@YU*1k{$iRTnr@x}`|5YkDLd;fMYXATM07*qoM6N<$ Ef)RolB>(^b literal 0 HcmV?d00001 diff --git a/src/Ombi/wwwroot/translations/bg.json b/src/Ombi/wwwroot/translations/bg.json index 13d352ae5..13e69e626 100644 --- a/src/Ombi/wwwroot/translations/bg.json +++ b/src/Ombi/wwwroot/translations/bg.json @@ -257,7 +257,6 @@ "Genres": "Genres", "TheatricalRelease": "Кинопремиера", "DigitalRelease": "Digital Release", - "UserScore": "User Score", "Votes": "Votes", "Runtime": "Runtime", "Minutes": "{{runtime}} Minutes", diff --git a/src/Ombi/wwwroot/translations/da.json b/src/Ombi/wwwroot/translations/da.json index 917882e24..a8948554a 100644 --- a/src/Ombi/wwwroot/translations/da.json +++ b/src/Ombi/wwwroot/translations/da.json @@ -257,7 +257,6 @@ "Genres": "Genres", "TheatricalRelease": "Biografudgivelse", "DigitalRelease": "Digital Release", - "UserScore": "User Score", "Votes": "Votes", "Runtime": "Runtime", "Minutes": "{{runtime}} Minutes", diff --git a/src/Ombi/wwwroot/translations/de.json b/src/Ombi/wwwroot/translations/de.json index b661f874d..e2678ff67 100644 --- a/src/Ombi/wwwroot/translations/de.json +++ b/src/Ombi/wwwroot/translations/de.json @@ -257,7 +257,6 @@ "Genres": "Genres", "TheatricalRelease": "Kinostart", "DigitalRelease": "Digital Release", - "UserScore": "User Score", "Votes": "Votes", "Runtime": "Runtime", "Minutes": "{{runtime}} Minutes", diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index a8f44ec5f..ff1985637 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -256,9 +256,8 @@ "RootFolderOverride":"Root Folder Override", "QualityOverride":"Quality Override", "Genres":"Genres", - "TheatricalRelease":"Theatrical Release", + "TheatricalRelease":"Release", "DigitalRelease":"Digital Release", - "UserScore":"User Score", "Votes":"Votes", "Runtime":"Runtime", "Minutes": "{{runtime}} Minutes", diff --git a/src/Ombi/wwwroot/translations/es.json b/src/Ombi/wwwroot/translations/es.json index f0684f01e..b7e7c62e6 100644 --- a/src/Ombi/wwwroot/translations/es.json +++ b/src/Ombi/wwwroot/translations/es.json @@ -257,7 +257,6 @@ "Genres": "Géneros", "TheatricalRelease": "En cines", "DigitalRelease": "Estreno Digital", - "UserScore": "Puntuación de Usuarios", "Votes": "Votos", "Runtime": "Duración", "Minutes": "{{runtime}} Minutos", diff --git a/src/Ombi/wwwroot/translations/fr.json b/src/Ombi/wwwroot/translations/fr.json index 86ee34fbd..39020a38d 100644 --- a/src/Ombi/wwwroot/translations/fr.json +++ b/src/Ombi/wwwroot/translations/fr.json @@ -257,7 +257,6 @@ "Genres": "Genres", "TheatricalRelease": "Sortie en salle", "DigitalRelease": "Sorti en Numérique", - "UserScore": "Note des Spectateurs", "Votes": "Votes", "Runtime": "Durée de visionnage", "Minutes": "{{runtime}} Minutes", diff --git a/src/Ombi/wwwroot/translations/hu.json b/src/Ombi/wwwroot/translations/hu.json index 8c1c3f3e5..fb6829ab4 100644 --- a/src/Ombi/wwwroot/translations/hu.json +++ b/src/Ombi/wwwroot/translations/hu.json @@ -257,7 +257,6 @@ "Genres": "Genres", "TheatricalRelease": "Mozis kiadás", "DigitalRelease": "Digital Release", - "UserScore": "User Score", "Votes": "Votes", "Runtime": "Runtime", "Minutes": "{{runtime}} Minutes", diff --git a/src/Ombi/wwwroot/translations/it.json b/src/Ombi/wwwroot/translations/it.json index 2f168bc23..3fb41154c 100644 --- a/src/Ombi/wwwroot/translations/it.json +++ b/src/Ombi/wwwroot/translations/it.json @@ -257,7 +257,6 @@ "Genres": "Generi", "TheatricalRelease": "Rilascio Teatrale", "DigitalRelease": "Rilascio Digitale", - "UserScore": "Punteggio Utente", "Votes": "Voti", "Runtime": "Durata", "Minutes": "{{runtime}} Minuti", diff --git a/src/Ombi/wwwroot/translations/nl.json b/src/Ombi/wwwroot/translations/nl.json index 0490be1e5..17ef0d1bb 100644 --- a/src/Ombi/wwwroot/translations/nl.json +++ b/src/Ombi/wwwroot/translations/nl.json @@ -257,7 +257,6 @@ "Genres": "Genres", "TheatricalRelease": "Bioscoop Uitgave", "DigitalRelease": "Digital Release", - "UserScore": "User Score", "Votes": "Votes", "Runtime": "Runtime", "Minutes": "{{runtime}} Minutes", diff --git a/src/Ombi/wwwroot/translations/no.json b/src/Ombi/wwwroot/translations/no.json index f74766883..80d32f716 100644 --- a/src/Ombi/wwwroot/translations/no.json +++ b/src/Ombi/wwwroot/translations/no.json @@ -257,7 +257,6 @@ "Genres": "Genres", "TheatricalRelease": "Kinopremiere", "DigitalRelease": "Digital Release", - "UserScore": "User Score", "Votes": "Votes", "Runtime": "Runtime", "Minutes": "{{runtime}} Minutes", diff --git a/src/Ombi/wwwroot/translations/pl.json b/src/Ombi/wwwroot/translations/pl.json index ca8ed5410..32e82515d 100644 --- a/src/Ombi/wwwroot/translations/pl.json +++ b/src/Ombi/wwwroot/translations/pl.json @@ -257,7 +257,6 @@ "Genres": "Genres", "TheatricalRelease": "Premiera kinowa", "DigitalRelease": "Digital Release", - "UserScore": "User Score", "Votes": "Votes", "Runtime": "Runtime", "Minutes": "{{runtime}} Minutes", diff --git a/src/Ombi/wwwroot/translations/pt.json b/src/Ombi/wwwroot/translations/pt.json index ddef26615..dbfb1f9bd 100644 --- a/src/Ombi/wwwroot/translations/pt.json +++ b/src/Ombi/wwwroot/translations/pt.json @@ -257,7 +257,6 @@ "Genres": "Genres", "TheatricalRelease": "Theatrical Release", "DigitalRelease": "Digital Release", - "UserScore": "User Score", "Votes": "Votes", "Runtime": "Runtime", "Minutes": "{{runtime}} Minutes", diff --git a/src/Ombi/wwwroot/translations/ru.json b/src/Ombi/wwwroot/translations/ru.json index 0d31783ee..789038220 100644 --- a/src/Ombi/wwwroot/translations/ru.json +++ b/src/Ombi/wwwroot/translations/ru.json @@ -257,7 +257,6 @@ "Genres": "Genres", "TheatricalRelease": "Релиз в кинотеатрах", "DigitalRelease": "Digital Release", - "UserScore": "User Score", "Votes": "Votes", "Runtime": "Runtime", "Minutes": "{{runtime}} Minutes", diff --git a/src/Ombi/wwwroot/translations/sk.json b/src/Ombi/wwwroot/translations/sk.json index 3ab7f6257..4cc9b96b6 100644 --- a/src/Ombi/wwwroot/translations/sk.json +++ b/src/Ombi/wwwroot/translations/sk.json @@ -257,7 +257,6 @@ "Genres": "Genres", "TheatricalRelease": "Kino vydanie", "DigitalRelease": "Digital Release", - "UserScore": "User Score", "Votes": "Votes", "Runtime": "Runtime", "Minutes": "{{runtime}} Minutes", diff --git a/src/Ombi/wwwroot/translations/sv.json b/src/Ombi/wwwroot/translations/sv.json index 4984b658d..63c92b817 100644 --- a/src/Ombi/wwwroot/translations/sv.json +++ b/src/Ombi/wwwroot/translations/sv.json @@ -257,7 +257,6 @@ "Genres": "Genrer", "TheatricalRelease": "Biopremiär", "DigitalRelease": "Digital release", - "UserScore": "Användarbetyg", "Votes": "Röster", "Runtime": "Speltid", "Minutes": "{{runtime}} minuter", From fcc87740bf56387cbf8085ba16c7bd3838e3b769 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Mon, 4 Jan 2021 22:57:54 +0000 Subject: [PATCH 63/82] Added some extra caching to reduce DB calls --- src/Ombi/Extensions/StartupExtensions.cs | 11 ++++++++--- src/Ombi/Startup.cs | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Ombi/Extensions/StartupExtensions.cs b/src/Ombi/Extensions/StartupExtensions.cs index c36c32a40..860ab40fb 100644 --- a/src/Ombi/Extensions/StartupExtensions.cs +++ b/src/Ombi/Extensions/StartupExtensions.cs @@ -76,7 +76,7 @@ namespace Ombi DemoSingleton.Instance.Demo = enabledDemo; } - public static void AddJwtAuthentication(this IServiceCollection services, IConfigurationRoot configuration) + public static void AddJwtAuthentication(this IServiceCollection services) { var tokenValidationParameters = new TokenValidationParameters { @@ -117,12 +117,17 @@ namespace Ombi OnTokenValidated = async context => { var userid = context.Principal?.Claims?.Where(x => x.Type.Equals("id", StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault()?.Value ?? default; - var um = context.HttpContext.RequestServices.GetRequiredService(); - var user = await um.FindByIdAsync(userid); + var cache = context.HttpContext.RequestServices.GetRequiredService(); + var user = await cache.GetOrAdd(userid + "token", async () => + { + var um = context.HttpContext.RequestServices.GetRequiredService(); + return await um.FindByIdAsync(userid); + }, DateTime.UtcNow.AddMinutes(10)); if (user == null) { context.Fail("invaild token"); } + } }; }); diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index 49227a344..2ed2b49e4 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -86,7 +86,7 @@ namespace Ombi services.AddMemoryCache(); services.AddHttpClient(); - services.AddJwtAuthentication(Configuration); + services.AddJwtAuthentication(); services.AddMvc() .AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore); From 6dba4eb0a4b82d180e0cde2f80dd806cf197f2f0 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 5 Jan 2021 08:13:37 +0000 Subject: [PATCH 64/82] Fixed #3957 --- src/Ombi/ClientApp/src/app/interfaces/IUser.ts | 1 + .../src/app/usermanagement/usermanagement.component.html | 3 ++- src/Ombi/appsettings.Development.json | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/interfaces/IUser.ts b/src/Ombi/ClientApp/src/app/interfaces/IUser.ts index cc8fdfea2..4b823a5be 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/IUser.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/IUser.ts @@ -53,6 +53,7 @@ export enum UserType { LocalUser = 1, PlexUser = 2, EmbyUser = 3, + EmbyConnect = 4, JellyfinUser = 5, } diff --git a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html index 60bf8589c..0684427b8 100644 --- a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html +++ b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.html @@ -88,7 +88,8 @@ Local User Plex User Emby User - Jellyfin User + Emby Connect User + Jellyfin User diff --git a/src/Ombi/appsettings.Development.json b/src/Ombi/appsettings.Development.json index e53bccc77..fbee78008 100644 --- a/src/Ombi/appsettings.Development.json +++ b/src/Ombi/appsettings.Development.json @@ -2,9 +2,9 @@ "Logging": { "IncludeScopes": false, "LogLevel": { - "Default": "Trace", - "System": "Trace", - "Microsoft": "Warning", + "Default": "Information", + "System": "Information", + "Microsoft": "Information", "System.Net.Http.HttpClient.health-checks": "Information", "HealthChecks": "Information" } From 88453f0a990bfb46a4b977262ce14145e22b6ce9 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 5 Jan 2021 16:35:40 +0000 Subject: [PATCH 65/82] Updated the discover cards, there is now a banner with the request status. --- .../card/discover-card.component.html | 8 +- .../card/discover-card.component.scss | 121 +++++++++++++++--- .../card/discover-card.component.ts | 30 ++++- src/Ombi/appsettings.Development.json | 2 +- 4 files changed, 137 insertions(+), 24 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html index 771040f44..ee3e59783 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html @@ -1,7 +1,11 @@ -
+
+
+ + {{getAvailbilityStatus()}} +
- {{result.title}} + {{result.title}} diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.scss b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.scss index cfb8e6494..75bc29d72 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.scss +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.scss @@ -22,24 +22,6 @@ $card-background: #2b2b2b; font-weight: bold; } -$border-width: 3px; - -.available { - border-bottom: $border-width #1DE9B6 solid; -} - -.approved { - border-bottom: $border-width #ff5722 solid; -} - -.requested { - border-bottom: $border-width #ffd740 solid; -} - -.notrequested { - border-bottom: $border-width #303030 solid; -} - .expand { text-align: center; } @@ -84,4 +66,105 @@ small { object-fit: cover; display: block; } -} \ No newline at end of file +} + + + +.box { + position: relative; + max-width: 600px; + box-shadow: 0 0 15px rgba(0,0,0,.1); + } + + /* common */ + .ribbon { + width: 118px; + height: 96px; + overflow: hidden; + position: absolute; + } + .ribbon::before, + .ribbon::after { + position: absolute; + z-index: -1; + content: ''; + display: none; + border: 5px solid black; + } + .ribbon span { + position: absolute; + display: none; + width: 180px; + padding: 10px 0; + background-color: black; + box-shadow: 0 5px 10px rgba(0,0,0,.1); + color: #fff; + font: 500 11px/1 'Lato', sans-serif; + text-shadow: 0 1px 1px rgba(0,0,0,.2); + text-transform: uppercase; + text-align: center; + } + + .ribbon.available span { + background-color: #1DE9B6 !important; + display: block; + color: #2f2f2f; + } + + .ribbon.available::before, + .ribbon.available::after { + border: 5px solid #1DE9B6 !important; + display: block; + } + + .ribbon.approved span { + background-color: #ff5722 !important; + display: block; + color: #2f2f2f; + } + + .ribbon.approved::before, + .ribbon.approved::after { + border: 5px solid #ff5722 !important; + display: block; + } + + .ribbon.requested span { + background-color: #ffd740 !important; + display: block; + color: #2f2f2f; + } + + .ribbon.requested::before, + .ribbon.requested::after { + border: 5px solid #ffd740 !important; + display: block; + } + + + /* top right*/ + .ribbon-top-right { + top: -10px; + right: -10px; + } + .ribbon-top-right::before, + .ribbon-top-right::after { + border-top-color: transparent; + border-right-color: transparent; + } + .ribbon-top-right::before { + top: 0; + left: 40px; + } + .ribbon-top-right::after { + bottom: 18px; + right: 0; + } + .ribbon-top-right span { + left: 0px; + top: 13px; + transform: rotate(45deg); + } + .ribbon-icon { + transform: translateX(0%) translateY(0%) rotate(-45deg); + } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts index cb401b8d1..335e88649 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit, Input } from "@angular/core"; import { IDiscoverCardResult } from "../../interfaces"; -import { RequestType, ISearchTvResult, ISearchMovieResult } from "../../../interfaces"; +import { RequestType } from "../../../interfaces"; import { SearchV2Service } from "../../../services"; import { MatDialog } from "@angular/material/dialog"; import { DiscoverCardDetailsComponent } from "./discover-card-details.component"; @@ -49,7 +49,33 @@ export class DiscoverCardComponent implements OnInit { if (this.result.requested) { return "requested"; } - return "notrequested"; + return ""; + } + + public getAvailbility(): string { + if (this.result.available) { + return "fa-check-circle"; + } + if (this.result.approved) { + return "fa-check-circle"; + } + if (this.result.requested) { + return "fa-question-circle"; + } + return ""; + } + + public getAvailbilityStatus(): string { + if (this.result.available) { + return "Available"; + } + if (this.result.approved) { + return "Approved"; + } + if (this.result.requested) { + return "Pending"; + } + return ""; } private getExtraMovieInfo() { diff --git a/src/Ombi/appsettings.Development.json b/src/Ombi/appsettings.Development.json index fbee78008..645fb3aaa 100644 --- a/src/Ombi/appsettings.Development.json +++ b/src/Ombi/appsettings.Development.json @@ -4,7 +4,7 @@ "LogLevel": { "Default": "Information", "System": "Information", - "Microsoft": "Information", + "Microsoft": "Warning", "System.Net.Http.HttpClient.health-checks": "Information", "HealthChecks": "Information" } From ea7307ac07cbea356ae1139c879b9e0959607f23 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 6 Jan 2021 16:42:15 +0000 Subject: [PATCH 66/82] We now show streaming information on the details page --- .../WatchProviderParserTests.cs | 94 ++ src/Ombi.Core/Engine/BaseMediaEngine.cs | 8 + .../Engine/Interfaces/IMovieEngineV2.cs | 1 + .../Engine/Interfaces/ITvSearchEngineV2.cs | 5 +- .../Engine/V2/MovieSearchEngineV2.cs | 20 + src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs | 41 +- src/Ombi.Core/Helpers/WatchProviderParser.cs | 35 + .../Models/Search/V2/StreamingData.cs | 9 + src/Ombi.Core/Models/UI/UserViewModel.cs | 1 + src/Ombi.Store/Entities/OmbiUser.cs | 12 +- src/Ombi.Store/Migration.md | 23 - ...106132735_UserStreamingCountry.Designer.cs | 1225 +++++++++++++++++ .../20210106132735_UserStreamingCountry.cs | 26 + .../OmbiMySqlContextModelSnapshot.cs | 107 +- ...106134000_UserStreamingCountry.Designer.cs | 1225 +++++++++++++++++ .../20210106134000_UserStreamingCountry.cs | 26 + .../OmbiSqliteContextModelSnapshot.cs | 106 +- src/Ombi.TheMovieDbApi/IMovieDbApi.cs | 4 +- .../Models/WatchProviders.cs | 77 ++ src/Ombi.TheMovieDbApi/TheMovieDbApi.cs | 50 +- .../ClientApp/src/app/interfaces/IStreams.ts | 5 + .../ClientApp/src/app/interfaces/IUser.ts | 7 +- .../movie-information-panel.component.html | 98 +- .../movie-information-panel.component.ts | 4 + .../tv-information-panel.component.html | 11 +- .../tv-information-panel.component.ts | 4 + .../media-details.component.scss | 6 + .../src/app/services/identity.service.ts | 12 +- .../src/app/services/searchV2.service.ts | 9 + .../ClientApp/src/app/shared/shared.module.ts | 2 +- .../user-preference.component.html | 54 +- .../user-preference.component.ts | 20 +- .../usermanagement-user.component.html | 8 + .../usermanagement-user.component.ts | 9 +- src/Ombi/Controllers/V1/IdentityController.cs | 45 +- src/Ombi/Controllers/V2/SearchController.cs | 15 + .../Identity/CountryStreamingPreference.cs | 7 + src/Ombi/wwwroot/translations/en.json | 10 +- 38 files changed, 3267 insertions(+), 154 deletions(-) create mode 100644 src/Ombi.Core.Tests/WatchProviderParserTests.cs create mode 100644 src/Ombi.Core/Helpers/WatchProviderParser.cs create mode 100644 src/Ombi.Core/Models/Search/V2/StreamingData.cs create mode 100644 src/Ombi.Store/Migrations/OmbiMySql/20210106132735_UserStreamingCountry.Designer.cs create mode 100644 src/Ombi.Store/Migrations/OmbiMySql/20210106132735_UserStreamingCountry.cs create mode 100644 src/Ombi.Store/Migrations/OmbiSqlite/20210106134000_UserStreamingCountry.Designer.cs create mode 100644 src/Ombi.Store/Migrations/OmbiSqlite/20210106134000_UserStreamingCountry.cs create mode 100644 src/Ombi.TheMovieDbApi/Models/WatchProviders.cs create mode 100644 src/Ombi/ClientApp/src/app/interfaces/IStreams.ts create mode 100644 src/Ombi/Models/Identity/CountryStreamingPreference.cs diff --git a/src/Ombi.Core.Tests/WatchProviderParserTests.cs b/src/Ombi.Core.Tests/WatchProviderParserTests.cs new file mode 100644 index 000000000..c56f64946 --- /dev/null +++ b/src/Ombi.Core.Tests/WatchProviderParserTests.cs @@ -0,0 +1,94 @@ +using NUnit.Framework; +using Ombi.Api.TheMovieDb.Models; +using Ombi.Core.Helpers; +using Ombi.Store.Entities; +using System.Collections.Generic; + +namespace Ombi.Core.Tests +{ + [TestFixture] + public class WatchProviderParserTests + { + [TestCase("GB", TestName = "UpperCase")] + [TestCase("gb", TestName = "LowerCase")] + [TestCase("gB", TestName = "MixedCase")] + public void GetValidStreamData(string streamingCountry) + { + var result = WatchProviderParser.GetUserWatchProviders(new WatchProviders + { + Results = new Results + { + GB = new WatchProviderData() + { + StreamInformation = new List + { + new StreamData + { + provider_name = "Netflix", + display_priority = 0, + logo_path = "logo", + provider_id = 8 + } + } + } + } + }, new OmbiUser { StreamingCountry = streamingCountry }); + + Assert.That(result[0].provider_name, Is.EqualTo("Netflix")); + } + + [TestCase("GB", TestName = "Missing_UpperCase")] + [TestCase("gb", TestName = "Missing_LowerCase")] + [TestCase("gB", TestName = "Missing_MixedCase")] + public void GetMissingStreamData(string streamingCountry) + { + var result = WatchProviderParser.GetUserWatchProviders(new WatchProviders + { + Results = new Results + { + AR = new WatchProviderData() + { + StreamInformation = new List + { + new StreamData + { + provider_name = "Netflix", + display_priority = 0, + logo_path = "logo", + provider_id = 8 + } + } + } + } + }, new OmbiUser { StreamingCountry = streamingCountry }); + + Assert.That(result, Is.Empty); + } + + [Test] + public void GetInvalidStreamData() + { + var result = WatchProviderParser.GetUserWatchProviders(new WatchProviders + { + Results = new Results + { + AR = new WatchProviderData() + { + StreamInformation = new List + { + new StreamData + { + provider_name = "Netflix", + display_priority = 0, + logo_path = "logo", + provider_id = 8 + } + } + } + } + }, new OmbiUser { StreamingCountry = "BLAH" }); + + Assert.That(result, Is.Empty); + } + } +} diff --git a/src/Ombi.Core/Engine/BaseMediaEngine.cs b/src/Ombi.Core/Engine/BaseMediaEngine.cs index fc9847c7d..66e60767a 100644 --- a/src/Ombi.Core/Engine/BaseMediaEngine.cs +++ b/src/Ombi.Core/Engine/BaseMediaEngine.cs @@ -15,6 +15,8 @@ using Ombi.Core.Settings; using Ombi.Settings.Settings.Models; using Ombi.Store.Entities; using Ombi.Store.Repository; +using Ombi.Api.TheMovieDb.Models; +using Ombi.Core.Helpers; namespace Ombi.Core.Engine { @@ -179,6 +181,12 @@ namespace Ombi.Core.Engine return user.Language; } + protected async Task> GetUserWatchProvider(WatchProviders providers) + { + var user = await GetUser(); + return WatchProviderParser.GetUserWatchProviders(providers, user); + } + private OmbiSettings ombiSettings; protected async Task GetOmbiSettings() { diff --git a/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs b/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs index 3b8d97dc0..746045ef3 100644 --- a/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs +++ b/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs @@ -26,5 +26,6 @@ namespace Ombi.Core.Engine.Interfaces int ResultLimit { get; set; } Task GetMovieInfoByImdbId(string imdbId, CancellationToken requestAborted); + Task> GetStreamInformation(int movieDbId, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs index a8a27aa19..d2201825f 100644 --- a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using Ombi.Core.Models.Search.V2; namespace Ombi.Core @@ -7,5 +9,6 @@ namespace Ombi.Core { Task GetShowInformation(int tvdbid); Task GetShowByRequest(int requestId); + Task> GetStreamInformation(int tvDbId, int tvMazeId, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs index 1e2110b38..dc009371a 100644 --- a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs @@ -249,6 +249,26 @@ namespace Ombi.Core.Engine.V2 return result; } + public async Task> GetStreamInformation(int movieDbId, CancellationToken cancellationToken) + { + var providers = await MovieApi.GetMovieWatchProviders(movieDbId, cancellationToken); + var results = await GetUserWatchProvider(providers); + + var data = new List(); + + foreach (var result in results) + { + data.Add(new StreamingData + { + Logo = result.logo_path, + Order = result.display_priority, + StreamingProvider = result.provider_name + }); + } + + return data; + } + protected async Task> TransformMovieResultsToResponse( IEnumerable movies) { diff --git a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs index dd2ce22aa..29ea01879 100644 --- a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs @@ -19,6 +19,8 @@ using Ombi.Core.Settings; using Ombi.Store.Repository; using TraktSharp.Entities; using Microsoft.EntityFrameworkCore; +using System.Threading; +using Ombi.Api.TheMovieDb; namespace Ombi.Core.Engine.V2 { @@ -27,15 +29,17 @@ namespace Ombi.Core.Engine.V2 private readonly ITvMazeApi _tvMaze; private readonly IMapper _mapper; private readonly ITraktApi _traktApi; + private readonly IMovieDbApi _movieApi; public TvSearchEngineV2(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper, ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ICacheService memCache, ISettingsService s, - IRepository sub) + IRepository sub, IMovieDbApi movieApi) : base(identity, service, r, um, memCache, s, sub) { _tvMaze = tvMaze; _mapper = mapper; _traktApi = trakt; + _movieApi = movieApi; } @@ -106,6 +110,39 @@ namespace Ombi.Core.Engine.V2 return await ProcessResult(mapped, traktInfoTask); } + public async Task> GetStreamInformation(int tvDbId, int tvMazeId, CancellationToken cancellationToken) + { + var tvdbshow = await Cache.GetOrAdd(nameof(GetShowInformation) + tvMazeId, + async () => await _tvMaze.ShowLookupByTheTvDbId(tvMazeId), DateTime.Now.AddHours(12)); + if (tvdbshow == null) + { + return null; + } + + /// this is a best effort guess since TV maze do not provide the TheMovieDbId + var movieDbResults = await _movieApi.SearchTv(tvdbshow.name, tvdbshow.premiered.Substring(0, 4)); + var potential = movieDbResults.FirstOrDefault(); + tvDbId = potential.Id; + // end guess + + var providers = await _movieApi.GetTvWatchProviders(tvDbId, cancellationToken); + var results = await GetUserWatchProvider(providers); + + var data = new List(); + + foreach (var result in results) + { + data.Add(new StreamingData + { + Logo = result.logo_path, + Order = result.display_priority, + StreamingProvider = result.provider_name + }); + } + + return data; + } + private IEnumerable ProcessResults(IEnumerable items) { var retVal = new List(); @@ -141,7 +178,7 @@ namespace Ombi.Core.Engine.V2 { item.Images.Medium = item.Images.Medium.ToHttpsUrl(); } - + if (item.Cast?.Any() ?? false) { foreach (var cast in item.Cast) diff --git a/src/Ombi.Core/Helpers/WatchProviderParser.cs b/src/Ombi.Core/Helpers/WatchProviderParser.cs new file mode 100644 index 000000000..68f3c26dd --- /dev/null +++ b/src/Ombi.Core/Helpers/WatchProviderParser.cs @@ -0,0 +1,35 @@ +using Ombi.Api.TheMovieDb.Models; +using Ombi.Store.Entities; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Ombi.Core.Helpers +{ + public static class WatchProviderParser + { + public static List GetUserWatchProviders(WatchProviders providers, OmbiUser user) + { + var data = new List(); + + if (providers?.Results == null) + { + return data; + } + + var resultsProp = providers.Results.GetType().GetProperties(); + var matchingStreamingCountry = resultsProp.FirstOrDefault(x => x.Name.Equals(user.StreamingCountry, StringComparison.InvariantCultureIgnoreCase)); + if (matchingStreamingCountry == null) + { + return data; + } + + var result = (WatchProviderData)matchingStreamingCountry.GetValue(providers.Results); + if (result == null || result.StreamInformation == null) + { + return data; + } + return result.StreamInformation; + } + } +} diff --git a/src/Ombi.Core/Models/Search/V2/StreamingData.cs b/src/Ombi.Core/Models/Search/V2/StreamingData.cs new file mode 100644 index 000000000..d9444c2ce --- /dev/null +++ b/src/Ombi.Core/Models/Search/V2/StreamingData.cs @@ -0,0 +1,9 @@ +namespace Ombi.Core.Models.Search.V2 +{ + public class StreamingData + { + public int Order { get; set; } + public string StreamingProvider { get; set; } + public string Logo { get; set; } + } +} diff --git a/src/Ombi.Core/Models/UI/UserViewModel.cs b/src/Ombi.Core/Models/UI/UserViewModel.cs index 0c3c9e349..0c9be846a 100644 --- a/src/Ombi.Core/Models/UI/UserViewModel.cs +++ b/src/Ombi.Core/Models/UI/UserViewModel.cs @@ -18,6 +18,7 @@ namespace Ombi.Core.Models.UI public UserType UserType { get; set; } public int MovieRequestLimit { get; set; } public int EpisodeRequestLimit { get; set; } + public string StreamingCountry { get; set; } public RequestQuotaCountModel EpisodeRequestQuota { get; set; } public RequestQuotaCountModel MovieRequestQuota { get; set; } public RequestQuotaCountModel MusicRequestQuota { get; set; } diff --git a/src/Ombi.Store/Entities/OmbiUser.cs b/src/Ombi.Store/Entities/OmbiUser.cs index 919d9a22c..46a49b1ae 100644 --- a/src/Ombi.Store/Entities/OmbiUser.cs +++ b/src/Ombi.Store/Entities/OmbiUser.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Microsoft.AspNetCore.Identity; using Newtonsoft.Json; -using Ombi.Helpers; namespace Ombi.Store.Entities { @@ -21,6 +21,12 @@ namespace Ombi.Store.Entities public string Language { get; set; } + /// + /// Used to get the Streaming information for media + /// + [Required] + public string StreamingCountry { get; set; } + public int? MovieRequestLimit { get; set; } public int? EpisodeRequestLimit { get; set; } public int? MusicRequestLimit { get; set; } @@ -40,14 +46,14 @@ namespace Ombi.Store.Entities public bool EmailLogin { get; set; } [NotMapped] public bool IsSystemUser => UserType == UserType.SystemUser; - + [JsonIgnore] public override string PasswordHash { get => base.PasswordHash; set => base.PasswordHash = value; } - + [JsonIgnore] public override string SecurityStamp { diff --git a/src/Ombi.Store/Migration.md b/src/Ombi.Store/Migration.md index 50711820b..6225229a3 100644 --- a/src/Ombi.Store/Migration.md +++ b/src/Ombi.Store/Migration.md @@ -14,29 +14,6 @@ If running migrations for any db provider other than Sqlite, then ensure the dat export PATH="$HOME/.dotnet/tools:$PATH" ``` -1. In `src/Ombi`, install the `Microsoft.EntityFrameworkCore.Design` package: - - ``` - cd src/Ombi - dotnet add package Microsoft.EntityFrameworkCore.Design - ``` - -1. For some reason, the `StartupSingleton.Instance.SecurityKey` in `src/Ombi/Extensions/StartupExtensions.cs` is invalid when running `dotnet ef migrations add` so we must fix it; apply this patch which seems to do the job: - - ``` - @@ -79,7 +79,7 @@ namespace Ombi - var tokenValidationParameters = new TokenValidationParameters - { - ValidateIssuerSigningKey = true, - - IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(StartupSingleton.Instance.SecurityKey)), - + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(StartupSingleton.Instance.SecurityKey + "s")), - RequireExpirationTime = true, - ValidateLifetime = true, - ValidAudience = "Ombi", - ``` - - *WARNING*: Don't forget to undo this before building Ombi, or things will be broken! - 1. List the available `dbcontext`s, and select the one that matches the database your fields will go in: ``` diff --git a/src/Ombi.Store/Migrations/OmbiMySql/20210106132735_UserStreamingCountry.Designer.cs b/src/Ombi.Store/Migrations/OmbiMySql/20210106132735_UserStreamingCountry.Designer.cs new file mode 100644 index 000000000..989ba1ba0 --- /dev/null +++ b/src/Ombi.Store/Migrations/OmbiMySql/20210106132735_UserStreamingCountry.Designer.cs @@ -0,0 +1,1225 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context.MySql; + +namespace Ombi.Store.Migrations.OmbiMySql +{ + [DbContext(typeof(OmbiMySqlContext))] + [Migration("20210106132735_UserStreamingCountry")] + partial class UserStreamingCountry + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Relational:MaxIdentifierLength", 64) + .HasAnnotation("ProductVersion", "5.0.1"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("RoleId") + .HasColumnType("varchar(255)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AuditArea") + .HasColumnType("int"); + + b.Property("AuditType") + .HasColumnType("int"); + + b.Property("DateTime") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("User") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.MobileDevices", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AddedAt") + .HasColumnType("datetime(6)"); + + b.Property("Token") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("MobileDevices"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Agent") + .HasColumnType("int"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("NotificationType") + .HasColumnType("int"); + + b.Property("Subject") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AddedAt") + .HasColumnType("datetime(6)"); + + b.Property("PlayerId") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("Alias") + .HasColumnType("longtext"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("EpisodeRequestLimit") + .HasColumnType("int"); + + b.Property("Language") + .HasColumnType("longtext"); + + b.Property("LastLoggedIn") + .HasColumnType("datetime(6)"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("MovieRequestLimit") + .HasColumnType("int"); + + b.Property("MusicRequestLimit") + .HasColumnType("int"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("longtext"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("ProviderUserId") + .HasColumnType("longtext"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("StreamingCountry") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserAccessToken") + .HasColumnType("longtext"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("UserType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AddedAt") + .HasColumnType("datetime(6)"); + + b.Property("AlbumId") + .HasColumnType("longtext"); + + b.Property("ContentId") + .HasColumnType("int"); + + b.Property("ContentType") + .HasColumnType("int"); + + b.Property("EpisodeNumber") + .HasColumnType("int"); + + b.Property("SeasonNumber") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestQueue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Completed") + .HasColumnType("datetime(6)"); + + b.Property("Dts") + .HasColumnType("datetime(6)"); + + b.Property("Error") + .HasColumnType("longtext"); + + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("RetryCount") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("RequestQueue"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("ArtistName") + .HasColumnType("longtext"); + + b.Property("Available") + .HasColumnType("tinyint(1)"); + + b.Property("Cover") + .HasColumnType("longtext"); + + b.Property("Denied") + .HasColumnType("tinyint(1)"); + + b.Property("DeniedReason") + .HasColumnType("longtext"); + + b.Property("Disk") + .HasColumnType("longtext"); + + b.Property("ForeignAlbumId") + .HasColumnType("longtext"); + + b.Property("ForeignArtistId") + .HasColumnType("longtext"); + + b.Property("MarkedAsApproved") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsAvailable") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsDenied") + .HasColumnType("datetime(6)"); + + b.Property("Rating") + .HasColumnType("decimal(65,30)"); + + b.Property("ReleaseDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("RequestedByAlias") + .HasColumnType("longtext"); + + b.Property("RequestedDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestedUserId") + .HasColumnType("varchar(255)"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("Available") + .HasColumnType("tinyint(1)"); + + b.Property("Denied") + .HasColumnType("tinyint(1)"); + + b.Property("DeniedReason") + .HasColumnType("longtext"); + + b.Property("IssueId") + .HasColumnType("int"); + + b.Property("MarkedAsApproved") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsAvailable") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsDenied") + .HasColumnType("datetime(6)"); + + b.Property("ParentRequestId") + .HasColumnType("int"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("RequestedByAlias") + .HasColumnType("longtext"); + + b.Property("RequestedDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestedUserId") + .HasColumnType("varchar(255)"); + + b.Property("SeriesType") + .HasColumnType("int"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Comment") + .HasColumnType("longtext"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("IssuesId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("IssueCategoryId") + .HasColumnType("int"); + + b.Property("IssueId") + .HasColumnType("int"); + + b.Property("ProviderId") + .HasColumnType("longtext"); + + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("ResovledDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Subject") + .HasColumnType("longtext"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.Property("UserReportedId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("Available") + .HasColumnType("tinyint(1)"); + + b.Property("Background") + .HasColumnType("longtext"); + + b.Property("Denied") + .HasColumnType("tinyint(1)"); + + b.Property("DeniedReason") + .HasColumnType("longtext"); + + b.Property("DigitalReleaseDate") + .HasColumnType("datetime(6)"); + + b.Property("ImdbId") + .HasColumnType("longtext"); + + b.Property("IssueId") + .HasColumnType("int"); + + b.Property("LangCode") + .HasColumnType("longtext"); + + b.Property("MarkedAsApproved") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsAvailable") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsDenied") + .HasColumnType("datetime(6)"); + + b.Property("Overview") + .HasColumnType("longtext"); + + b.Property("PosterPath") + .HasColumnType("longtext"); + + b.Property("QualityOverride") + .HasColumnType("int"); + + b.Property("ReleaseDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("RequestedByAlias") + .HasColumnType("longtext"); + + b.Property("RequestedDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestedUserId") + .HasColumnType("varchar(255)"); + + b.Property("RootPathOverride") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("longtext"); + + b.Property("TheMovieDbId") + .HasColumnType("int"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("EpisodeCount") + .HasColumnType("int"); + + b.Property("RequestDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Background") + .HasColumnType("longtext"); + + b.Property("ImdbId") + .HasColumnType("longtext"); + + b.Property("Overview") + .HasColumnType("longtext"); + + b.Property("PosterPath") + .HasColumnType("longtext"); + + b.Property("QualityOverride") + .HasColumnType("int"); + + b.Property("ReleaseDate") + .HasColumnType("datetime(6)"); + + b.Property("RootFolder") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("longtext"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.Property("TotalSeasons") + .HasColumnType("int"); + + b.Property("TvDbId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Token") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Agent") + .HasColumnType("int"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("RadarrQualityProfile") + .HasColumnType("int"); + + b.Property("RadarrRootPath") + .HasColumnType("int"); + + b.Property("SonarrQualityProfile") + .HasColumnType("int"); + + b.Property("SonarrQualityProfileAnime") + .HasColumnType("int"); + + b.Property("SonarrRootPath") + .HasColumnType("int"); + + b.Property("SonarrRootPathAnime") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Deleted") + .HasColumnType("tinyint(1)"); + + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("VoteType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AirDate") + .HasColumnType("datetime(6)"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("Available") + .HasColumnType("tinyint(1)"); + + b.Property("EpisodeNumber") + .HasColumnType("int"); + + b.Property("Requested") + .HasColumnType("tinyint(1)"); + + b.Property("SeasonId") + .HasColumnType("int"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.Property("Url") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ChildRequestId") + .HasColumnType("int"); + + b.Property("SeasonNumber") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ombi.Store.Entities.MobileDevices", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("ParentRequest"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Issues"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", null) + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests", null) + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + + b.Navigation("IssueCategory"); + + b.Navigation("UserReported"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildRequest"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Navigation("NotificationUserIds"); + + b.Navigation("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Navigation("Issues"); + + b.Navigation("SeasonRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Navigation("Comments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Navigation("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Navigation("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Navigation("Episodes"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiMySql/20210106132735_UserStreamingCountry.cs b/src/Ombi.Store/Migrations/OmbiMySql/20210106132735_UserStreamingCountry.cs new file mode 100644 index 000000000..1ee38e369 --- /dev/null +++ b/src/Ombi.Store/Migrations/OmbiMySql/20210106132735_UserStreamingCountry.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations.OmbiMySql +{ + public partial class UserStreamingCountry : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "StreamingCountry", + table: "AspNetUsers", + type: "longtext", + nullable: false, + defaultValue: "US"); + + migrationBuilder.Sql("UPDATE AspNetUsers SET StreamingCountry = 'US'"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "StreamingCountry", + table: "AspNetUsers"); + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiMySql/OmbiMySqlContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiMySql/OmbiMySqlContextModelSnapshot.cs index 1678a77c1..38487dc9f 100644 --- a/src/Ombi.Store/Migrations/OmbiMySql/OmbiMySqlContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiMySql/OmbiMySqlContextModelSnapshot.cs @@ -14,8 +14,8 @@ namespace Ombi.Store.Migrations.OmbiMySql { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.1.1") - .HasAnnotation("Relational:MaxIdentifierLength", 64); + .HasAnnotation("Relational:MaxIdentifierLength", 64) + .HasAnnotation("ProductVersion", "5.0.1"); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => { @@ -27,18 +27,18 @@ namespace Ombi.Store.Migrations.OmbiMySql .HasColumnType("longtext"); b.Property("Name") - .HasColumnType("varchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("varchar(256)"); b.Property("NormalizedName") - .HasColumnType("varchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("varchar(256)"); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() - .HasName("RoleNameIndex"); + .HasDatabaseName("RoleNameIndex"); b.ToTable("AspNetRoles"); }); @@ -257,8 +257,8 @@ namespace Ombi.Store.Migrations.OmbiMySql .HasColumnType("longtext"); b.Property("Email") - .HasColumnType("varchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("varchar(256)"); b.Property("EmailConfirmed") .HasColumnType("tinyint(1)"); @@ -285,12 +285,12 @@ namespace Ombi.Store.Migrations.OmbiMySql .HasColumnType("int"); b.Property("NormalizedEmail") - .HasColumnType("varchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("varchar(256)"); b.Property("NormalizedUserName") - .HasColumnType("varchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("varchar(256)"); b.Property("PasswordHash") .HasColumnType("longtext"); @@ -307,6 +307,9 @@ namespace Ombi.Store.Migrations.OmbiMySql b.Property("SecurityStamp") .HasColumnType("longtext"); + b.Property("StreamingCountry") + .HasColumnType("longtext"); + b.Property("TwoFactorEnabled") .HasColumnType("tinyint(1)"); @@ -314,8 +317,8 @@ namespace Ombi.Store.Migrations.OmbiMySql .HasColumnType("longtext"); b.Property("UserName") - .HasColumnType("varchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("varchar(256)"); b.Property("UserType") .HasColumnType("int"); @@ -323,11 +326,11 @@ namespace Ombi.Store.Migrations.OmbiMySql b.HasKey("Id"); b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); + .HasDatabaseName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() - .HasName("UserNameIndex"); + .HasDatabaseName("UserNameIndex"); b.ToTable("AspNetUsers"); }); @@ -1017,6 +1020,8 @@ namespace Ombi.Store.Migrations.OmbiMySql b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany() .HasForeignKey("UserId"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => @@ -1024,6 +1029,8 @@ namespace Ombi.Store.Migrations.OmbiMySql b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany("NotificationUserIds") .HasForeignKey("UserId"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => @@ -1031,6 +1038,8 @@ namespace Ombi.Store.Migrations.OmbiMySql b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany() .HasForeignKey("UserId"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => @@ -1038,6 +1047,8 @@ namespace Ombi.Store.Migrations.OmbiMySql b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") .WithMany() .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); }); modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => @@ -1051,6 +1062,10 @@ namespace Ombi.Store.Migrations.OmbiMySql b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") .WithMany() .HasForeignKey("RequestedUserId"); + + b.Navigation("ParentRequest"); + + b.Navigation("RequestedUser"); }); modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => @@ -1062,6 +1077,10 @@ namespace Ombi.Store.Migrations.OmbiMySql b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany() .HasForeignKey("UserId"); + + b.Navigation("Issues"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => @@ -1083,6 +1102,10 @@ namespace Ombi.Store.Migrations.OmbiMySql b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") .WithMany() .HasForeignKey("UserReportedId"); + + b.Navigation("IssueCategory"); + + b.Navigation("UserReported"); }); modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => @@ -1090,6 +1113,8 @@ namespace Ombi.Store.Migrations.OmbiMySql b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") .WithMany() .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); }); modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => @@ -1097,6 +1122,8 @@ namespace Ombi.Store.Migrations.OmbiMySql b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany() .HasForeignKey("UserId"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => @@ -1104,6 +1131,8 @@ namespace Ombi.Store.Migrations.OmbiMySql b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany() .HasForeignKey("UserId"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => @@ -1111,6 +1140,8 @@ namespace Ombi.Store.Migrations.OmbiMySql b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany("UserNotificationPreferences") .HasForeignKey("UserId"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => @@ -1118,6 +1149,8 @@ namespace Ombi.Store.Migrations.OmbiMySql b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany() .HasForeignKey("UserId"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Entities.Votes", b => @@ -1125,6 +1158,8 @@ namespace Ombi.Store.Migrations.OmbiMySql b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany() .HasForeignKey("UserId"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => @@ -1134,6 +1169,8 @@ namespace Ombi.Store.Migrations.OmbiMySql .HasForeignKey("SeasonId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + + b.Navigation("Season"); }); modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => @@ -1143,6 +1180,42 @@ namespace Ombi.Store.Migrations.OmbiMySql .HasForeignKey("ChildRequestId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + + b.Navigation("ChildRequest"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Navigation("NotificationUserIds"); + + b.Navigation("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Navigation("Issues"); + + b.Navigation("SeasonRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Navigation("Comments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Navigation("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Navigation("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Navigation("Episodes"); }); #pragma warning restore 612, 618 } diff --git a/src/Ombi.Store/Migrations/OmbiSqlite/20210106134000_UserStreamingCountry.Designer.cs b/src/Ombi.Store/Migrations/OmbiSqlite/20210106134000_UserStreamingCountry.Designer.cs new file mode 100644 index 000000000..e9c6e1f20 --- /dev/null +++ b/src/Ombi.Store/Migrations/OmbiSqlite/20210106134000_UserStreamingCountry.Designer.cs @@ -0,0 +1,1225 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context.Sqlite; + +namespace Ombi.Store.Migrations.OmbiSqlite +{ + [DbContext(typeof(OmbiSqliteContext))] + [Migration("20210106134000_UserStreamingCountry")] + partial class UserStreamingCountry + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.1"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AuditArea") + .HasColumnType("INTEGER"); + + b.Property("AuditType") + .HasColumnType("INTEGER"); + + b.Property("DateTime") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("User") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.MobileDevices", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("Token") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("MobileDevices"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Agent") + .HasColumnType("INTEGER"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("Message") + .HasColumnType("TEXT"); + + b.Property("NotificationType") + .HasColumnType("INTEGER"); + + b.Property("Subject") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("PlayerId") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("Alias") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("EpisodeRequestLimit") + .HasColumnType("INTEGER"); + + b.Property("Language") + .HasColumnType("TEXT"); + + b.Property("LastLoggedIn") + .HasColumnType("TEXT"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("MovieRequestLimit") + .HasColumnType("INTEGER"); + + b.Property("MusicRequestLimit") + .HasColumnType("INTEGER"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("ProviderUserId") + .HasColumnType("TEXT"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("StreamingCountry") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserAccessToken") + .HasColumnType("TEXT"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("UserType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("AlbumId") + .HasColumnType("TEXT"); + + b.Property("ContentId") + .HasColumnType("INTEGER"); + + b.Property("ContentType") + .HasColumnType("INTEGER"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestQueue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Completed") + .HasColumnType("TEXT"); + + b.Property("Dts") + .HasColumnType("TEXT"); + + b.Property("Error") + .HasColumnType("TEXT"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("RequestQueue"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("ArtistName") + .HasColumnType("TEXT"); + + b.Property("Available") + .HasColumnType("INTEGER"); + + b.Property("Cover") + .HasColumnType("TEXT"); + + b.Property("Denied") + .HasColumnType("INTEGER"); + + b.Property("DeniedReason") + .HasColumnType("TEXT"); + + b.Property("Disk") + .HasColumnType("TEXT"); + + b.Property("ForeignAlbumId") + .HasColumnType("TEXT"); + + b.Property("ForeignArtistId") + .HasColumnType("TEXT"); + + b.Property("MarkedAsApproved") + .HasColumnType("TEXT"); + + b.Property("MarkedAsAvailable") + .HasColumnType("TEXT"); + + b.Property("MarkedAsDenied") + .HasColumnType("TEXT"); + + b.Property("Rating") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("RequestedByAlias") + .HasColumnType("TEXT"); + + b.Property("RequestedDate") + .HasColumnType("TEXT"); + + b.Property("RequestedUserId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("Available") + .HasColumnType("INTEGER"); + + b.Property("Denied") + .HasColumnType("INTEGER"); + + b.Property("DeniedReason") + .HasColumnType("TEXT"); + + b.Property("IssueId") + .HasColumnType("INTEGER"); + + b.Property("MarkedAsApproved") + .HasColumnType("TEXT"); + + b.Property("MarkedAsAvailable") + .HasColumnType("TEXT"); + + b.Property("MarkedAsDenied") + .HasColumnType("TEXT"); + + b.Property("ParentRequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("RequestedByAlias") + .HasColumnType("TEXT"); + + b.Property("RequestedDate") + .HasColumnType("TEXT"); + + b.Property("RequestedUserId") + .HasColumnType("TEXT"); + + b.Property("SeriesType") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Comment") + .HasColumnType("TEXT"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("IssuesId") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDate") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("IssueCategoryId") + .HasColumnType("INTEGER"); + + b.Property("IssueId") + .HasColumnType("INTEGER"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("ResovledDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Subject") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("UserReportedId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("Available") + .HasColumnType("INTEGER"); + + b.Property("Background") + .HasColumnType("TEXT"); + + b.Property("Denied") + .HasColumnType("INTEGER"); + + b.Property("DeniedReason") + .HasColumnType("TEXT"); + + b.Property("DigitalReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("IssueId") + .HasColumnType("INTEGER"); + + b.Property("LangCode") + .HasColumnType("TEXT"); + + b.Property("MarkedAsApproved") + .HasColumnType("TEXT"); + + b.Property("MarkedAsAvailable") + .HasColumnType("TEXT"); + + b.Property("MarkedAsDenied") + .HasColumnType("TEXT"); + + b.Property("Overview") + .HasColumnType("TEXT"); + + b.Property("PosterPath") + .HasColumnType("TEXT"); + + b.Property("QualityOverride") + .HasColumnType("INTEGER"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("RequestedByAlias") + .HasColumnType("TEXT"); + + b.Property("RequestedDate") + .HasColumnType("TEXT"); + + b.Property("RequestedUserId") + .HasColumnType("TEXT"); + + b.Property("RootPathOverride") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("TEXT"); + + b.Property("TheMovieDbId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EpisodeCount") + .HasColumnType("INTEGER"); + + b.Property("RequestDate") + .HasColumnType("TEXT"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Background") + .HasColumnType("TEXT"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("Overview") + .HasColumnType("TEXT"); + + b.Property("PosterPath") + .HasColumnType("TEXT"); + + b.Property("QualityOverride") + .HasColumnType("INTEGER"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("RootFolder") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TotalSeasons") + .HasColumnType("INTEGER"); + + b.Property("TvDbId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Token") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Agent") + .HasColumnType("INTEGER"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("RadarrQualityProfile") + .HasColumnType("INTEGER"); + + b.Property("RadarrRootPath") + .HasColumnType("INTEGER"); + + b.Property("SonarrQualityProfile") + .HasColumnType("INTEGER"); + + b.Property("SonarrQualityProfileAnime") + .HasColumnType("INTEGER"); + + b.Property("SonarrRootPath") + .HasColumnType("INTEGER"); + + b.Property("SonarrRootPathAnime") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("Deleted") + .HasColumnType("INTEGER"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("VoteType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AirDate") + .HasColumnType("TEXT"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("Available") + .HasColumnType("INTEGER"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("Requested") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChildRequestId") + .HasColumnType("INTEGER"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ombi.Store.Entities.MobileDevices", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("ParentRequest"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Issues"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", null) + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests", null) + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + + b.Navigation("IssueCategory"); + + b.Navigation("UserReported"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildRequest"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Navigation("NotificationUserIds"); + + b.Navigation("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Navigation("Issues"); + + b.Navigation("SeasonRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Navigation("Comments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Navigation("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Navigation("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Navigation("Episodes"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiSqlite/20210106134000_UserStreamingCountry.cs b/src/Ombi.Store/Migrations/OmbiSqlite/20210106134000_UserStreamingCountry.cs new file mode 100644 index 000000000..3e5ff7651 --- /dev/null +++ b/src/Ombi.Store/Migrations/OmbiSqlite/20210106134000_UserStreamingCountry.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations.OmbiSqlite +{ + public partial class UserStreamingCountry : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "StreamingCountry", + table: "AspNetUsers", + type: "TEXT", + nullable: false, + defaultValue: "US"); + + migrationBuilder.Sql("UPDATE AspNetUsers SET StreamingCountry = 'US'"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "StreamingCountry", + table: "AspNetUsers"); + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiSqlite/OmbiSqliteContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiSqlite/OmbiSqliteContextModelSnapshot.cs index 7e9457be4..6a00fe29b 100644 --- a/src/Ombi.Store/Migrations/OmbiSqlite/OmbiSqliteContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiSqlite/OmbiSqliteContextModelSnapshot.cs @@ -14,7 +14,7 @@ namespace Ombi.Store.Migrations.OmbiSqlite { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.1.1"); + .HasAnnotation("ProductVersion", "5.0.1"); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => { @@ -26,18 +26,18 @@ namespace Ombi.Store.Migrations.OmbiSqlite .HasColumnType("TEXT"); b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("NormalizedName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() - .HasName("RoleNameIndex"); + .HasDatabaseName("RoleNameIndex"); b.ToTable("AspNetRoles"); }); @@ -256,8 +256,8 @@ namespace Ombi.Store.Migrations.OmbiSqlite .HasColumnType("TEXT"); b.Property("Email") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("EmailConfirmed") .HasColumnType("INTEGER"); @@ -284,12 +284,12 @@ namespace Ombi.Store.Migrations.OmbiSqlite .HasColumnType("INTEGER"); b.Property("NormalizedEmail") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("NormalizedUserName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("PasswordHash") .HasColumnType("TEXT"); @@ -306,6 +306,10 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.Property("SecurityStamp") .HasColumnType("TEXT"); + b.Property("StreamingCountry") + .IsRequired() + .HasColumnType("TEXT"); + b.Property("TwoFactorEnabled") .HasColumnType("INTEGER"); @@ -313,8 +317,8 @@ namespace Ombi.Store.Migrations.OmbiSqlite .HasColumnType("TEXT"); b.Property("UserName") - .HasColumnType("TEXT") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("TEXT"); b.Property("UserType") .HasColumnType("INTEGER"); @@ -322,11 +326,11 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.HasKey("Id"); b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); + .HasDatabaseName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() - .HasName("UserNameIndex"); + .HasDatabaseName("UserNameIndex"); b.ToTable("AspNetUsers"); }); @@ -1016,6 +1020,8 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany() .HasForeignKey("UserId"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => @@ -1023,6 +1029,8 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany("NotificationUserIds") .HasForeignKey("UserId"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => @@ -1030,6 +1038,8 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany() .HasForeignKey("UserId"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => @@ -1037,6 +1047,8 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") .WithMany() .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); }); modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => @@ -1050,6 +1062,10 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") .WithMany() .HasForeignKey("RequestedUserId"); + + b.Navigation("ParentRequest"); + + b.Navigation("RequestedUser"); }); modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => @@ -1061,6 +1077,10 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany() .HasForeignKey("UserId"); + + b.Navigation("Issues"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => @@ -1082,6 +1102,10 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") .WithMany() .HasForeignKey("UserReportedId"); + + b.Navigation("IssueCategory"); + + b.Navigation("UserReported"); }); modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => @@ -1089,6 +1113,8 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") .WithMany() .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); }); modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => @@ -1096,6 +1122,8 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany() .HasForeignKey("UserId"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => @@ -1103,6 +1131,8 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany() .HasForeignKey("UserId"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => @@ -1110,6 +1140,8 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany("UserNotificationPreferences") .HasForeignKey("UserId"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => @@ -1117,6 +1149,8 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany() .HasForeignKey("UserId"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Entities.Votes", b => @@ -1124,6 +1158,8 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany() .HasForeignKey("UserId"); + + b.Navigation("User"); }); modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => @@ -1133,6 +1169,8 @@ namespace Ombi.Store.Migrations.OmbiSqlite .HasForeignKey("SeasonId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + + b.Navigation("Season"); }); modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => @@ -1142,6 +1180,42 @@ namespace Ombi.Store.Migrations.OmbiSqlite .HasForeignKey("ChildRequestId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + + b.Navigation("ChildRequest"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Navigation("NotificationUserIds"); + + b.Navigation("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Navigation("Issues"); + + b.Navigation("SeasonRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Navigation("Comments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Navigation("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Navigation("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Navigation("Episodes"); }); #pragma warning restore 612, 618 } diff --git a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs index c9e21c2ec..1b35381ad 100644 --- a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs @@ -13,7 +13,7 @@ namespace Ombi.Api.TheMovieDb Task> NowPlaying(string languageCode, int? page = null); Task> PopularMovies(string languageCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken)); Task> SearchMovie(string searchTerm, int? year, string languageCode); - Task> SearchTv(string searchTerm); + Task> SearchTv(string searchTerm, string year = default); Task> TopRated(string languageCode, int? page = null); Task> Upcoming(string languageCode, int? page = null); Task> SimilarMovies(int movieId, string langCode); @@ -28,5 +28,7 @@ namespace Ombi.Api.TheMovieDb Task GetCollection(string langCode, int collectionId, CancellationToken cancellationToken); Task> SearchKeyword(string searchTerm); Task GetKeyword(int keywordId); + Task GetMovieWatchProviders(int theMoviedbId, CancellationToken token); + Task GetTvWatchProviders(int theMoviedbId, CancellationToken token); } } \ No newline at end of file diff --git a/src/Ombi.TheMovieDbApi/Models/WatchProviders.cs b/src/Ombi.TheMovieDbApi/Models/WatchProviders.cs new file mode 100644 index 000000000..bcd4c2418 --- /dev/null +++ b/src/Ombi.TheMovieDbApi/Models/WatchProviders.cs @@ -0,0 +1,77 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace Ombi.Api.TheMovieDb.Models +{ + public class WatchProviders + { + [JsonProperty("id")] + public int Id { get; set; } + [JsonProperty("results")] + public Results Results { get; set; } + } + + public class Results + { + public WatchProviderData AR { get; set; } + public WatchProviderData AT { get; set; } + public WatchProviderData AU { get; set; } + public WatchProviderData BE { get; set; } + public WatchProviderData BR { get; set; } + public WatchProviderData CA { get; set; } + public WatchProviderData CH { get; set; } + public WatchProviderData CL { get; set; } + public WatchProviderData CO { get; set; } + public WatchProviderData CZ { get; set; } + public WatchProviderData DE { get; set; } + public WatchProviderData DK { get; set; } + public WatchProviderData EC { get; set; } + public WatchProviderData EE { get; set; } + public WatchProviderData ES { get; set; } + public WatchProviderData FI { get; set; } + public WatchProviderData FR { get; set; } + public WatchProviderData GB { get; set; } + public WatchProviderData GR { get; set; } + public WatchProviderData HU { get; set; } + public WatchProviderData ID { get; set; } + public WatchProviderData IE { get; set; } + public WatchProviderData IN { get; set; } + public WatchProviderData IT { get; set; } + public WatchProviderData JP { get; set; } + public WatchProviderData KR { get; set; } + public WatchProviderData LT { get; set; } + public WatchProviderData LV { get; set; } + public WatchProviderData MX { get; set; } + public WatchProviderData MY { get; set; } + public WatchProviderData NL { get; set; } + public WatchProviderData NO { get; set; } + public WatchProviderData NZ { get; set; } + public WatchProviderData PE { get; set; } + public WatchProviderData PH { get; set; } + public WatchProviderData PL { get; set; } + public WatchProviderData PT { get; set; } + public WatchProviderData RU { get; set; } + public WatchProviderData SE { get; set; } + public WatchProviderData SG { get; set; } + public WatchProviderData TH { get; set; } + public WatchProviderData TR { get; set; } + public WatchProviderData US { get; set; } + public WatchProviderData VE { get; set; } + public WatchProviderData ZA { get; set; } + } + + public class WatchProviderData + { + public string link { get; set; } + [JsonProperty("flatrate")] + public List StreamInformation { get; set; } + } + + public class StreamData + { + public int display_priority { get; set; } + public string logo_path { get; set; } + public int provider_id { get; set; } + public string provider_name { get; set; } + } +} diff --git a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs index c9deede55..f270ad3b2 100644 --- a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs @@ -10,6 +10,7 @@ using Nito.AsyncEx; using Ombi.Api.TheMovieDb.Models; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; +using Ombi.Helpers; using Ombi.TheMovieDbApi.Models; namespace Ombi.Api.TheMovieDb @@ -24,7 +25,7 @@ namespace Ombi.Api.TheMovieDb } private const string ApiToken = "b8eabaf5608b88d0298aa189dd90bf00"; - private const string BaseUri ="http://api.themoviedb.org/3/"; + private const string BaseUri = "http://api.themoviedb.org/3/"; private IMapper Mapper { get; } private IApi Api { get; } private AsyncLazy Settings { get; } @@ -107,11 +108,15 @@ namespace Ombi.Api.TheMovieDb return result; } - public async Task> SearchTv(string searchTerm) + public async Task> SearchTv(string searchTerm, string year = default) { var request = new Request($"search/tv", BaseUri, HttpMethod.Get); request.AddQueryString("api_key", ApiToken); request.AddQueryString("query", searchTerm); + if (year.HasValue()) + { + request.AddQueryString("first_air_date_year", year); + } AddRetry(request); var result = await Api.Request>(request); @@ -126,7 +131,7 @@ namespace Ombi.Api.TheMovieDb return await Api.Request(request); } - + public async Task> SimilarMovies(int movieId, string langCode) { var request = new Request($"movie/{movieId}/similar", BaseUri, HttpMethod.Get); @@ -165,7 +170,7 @@ namespace Ombi.Api.TheMovieDb AddRetry(request); - var result = await Api.Request>(request); + var result = await Api.Request>(request); return Mapper.Map>(result.results); } @@ -299,6 +304,32 @@ namespace Ombi.Api.TheMovieDb return keyword == null || keyword.Id == 0 ? null : keyword; } + public Task> MultiSearch(string searchTerm, string languageCode, CancellationToken cancellationToken) + { + var request = new Request("search/multi", BaseUri, HttpMethod.Get); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("language", languageCode); + request.AddQueryString("query", searchTerm); + var result = Api.Request>(request, cancellationToken); + return result; + } + + public Task GetMovieWatchProviders(int theMoviedbId, CancellationToken token) + { + var request = new Request($"movie/{theMoviedbId}/watch/providers", BaseUri, HttpMethod.Get); + request.AddQueryString("api_key", ApiToken); + + return Api.Request(request, token); + } + + public Task GetTvWatchProviders(int theMoviedbId, CancellationToken token) + { + var request = new Request($"tv/{theMoviedbId}/watch/providers", BaseUri, HttpMethod.Get); + request.AddQueryString("api_key", ApiToken); + + return Api.Request(request, token); + } + private async Task AddDiscoverMovieSettings(Request request) { var settings = await Settings; @@ -309,17 +340,6 @@ namespace Ombi.Api.TheMovieDb } } - - public async Task> MultiSearch(string searchTerm, string languageCode, CancellationToken cancellationToken) - { - var request = new Request("search/multi", BaseUri, HttpMethod.Get); - request.AddQueryString("api_key", ApiToken); - request.AddQueryString("language", languageCode); - request.AddQueryString("query", searchTerm); - var result = await Api.Request>(request, cancellationToken); - return result; - } - private static void AddRetry(Request request) { request.Retry = true; diff --git a/src/Ombi/ClientApp/src/app/interfaces/IStreams.ts b/src/Ombi/ClientApp/src/app/interfaces/IStreams.ts new file mode 100644 index 000000000..b666e01f6 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/interfaces/IStreams.ts @@ -0,0 +1,5 @@ +export interface IStreamingData { + order: number; + streamingProvider: string; + logo: string; +} diff --git a/src/Ombi/ClientApp/src/app/interfaces/IUser.ts b/src/Ombi/ClientApp/src/app/interfaces/IUser.ts index 4b823a5be..856d57f0a 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/IUser.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/IUser.ts @@ -17,6 +17,7 @@ export interface IUser { userAccessToken: string; language: string; userQualityProfiles: IUserQualityProfiles; + streamingCountry: string; // FOR UI episodeRequestQuota: IRemainingRequests | null; @@ -35,7 +36,7 @@ export interface IUserQualityProfiles { sonarrRootPath: number; sonarrQualityProfile: number; radarrRootPath: number; - radarrQualityProfile: number; + radarrQualityProfile: number; } export interface ICreateWizardUser { @@ -49,6 +50,10 @@ export interface IWizardUserResult { errors: string[]; } +export interface IStreamingCountries { + code: string; +} + export enum UserType { LocalUser = 1, PlexUser = 2, diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html index 62a458c19..479644203 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html @@ -1,13 +1,27 @@
- + {{movie.voteAverage | number:'1.0-1'}}/10 - {{ratings.critics_score}}% + + {{ratings.critics_score}}% - {{ratings.audience_score}}% + + {{ratings.audience_score}}% +
+
+ {{'MediaDetails.StreamingOn' | translate }}: +
+ + + +
+

{{'MediaDetails.Status' | translate }}: @@ -59,48 +73,48 @@
{{'MediaDetails.TheatricalRelease' | translate }}: - {{movie.releaseDate | date: 'mediumDate'}} + {{movie.releaseDate | date: 'mediumDate'}} -
- {{'MediaDetails.DigitalRelease' | translate }}: - {{movie.digitalReleaseDate | date: 'mediumDate'}} -
+
+ {{'MediaDetails.DigitalRelease' | translate }}: + {{movie.digitalReleaseDate | date: 'mediumDate'}} +
-
- {{'MediaDetails.Votes' | translate }}: - {{movie.voteCount | thousandShort: 1}} -
+
+ {{'MediaDetails.Votes' | translate }}: + {{movie.voteCount | thousandShort: 1}} +
+
+ {{'MediaDetails.Runtime' | translate }}: + {{'MediaDetails.Minutes' | translate:{runtime: movie.runtime} }} +
+
+ {{'MediaDetails.Revenue' | translate }}: + {{movie.revenue | currency: 'USD'}} +
+
+ {{'MediaDetails.Budget' | translate }}: + {{movie.budget | currency: 'USD'}} +
+ +
+
+ {{'MediaDetails.Genres' | translate }}:
- {{'MediaDetails.Runtime' | translate }}: - {{'MediaDetails.Minutes' | translate:{runtime: movie.runtime} }} -
-
- {{'MediaDetails.Revenue' | translate }}: - {{movie.revenue | currency: 'USD'}} -
-
- {{'MediaDetails.Budget' | translate }}: - {{movie.budget | currency: 'USD'}} -
- -
-
- {{'MediaDetails.Genres' | translate }}: -
- - - {{genre.name}} - - -
-
- -
-
- {{'MediaDetails.Keywords' | translate }}: - - {{keyword.name}} + + {{genre.name}} -
\ No newline at end of file +
+
+ +
+
+ {{'MediaDetails.Keywords' | translate }}: + + + {{keyword.name}} + + +
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.ts index 1df97372c..89d9c076c 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.ts @@ -4,6 +4,7 @@ import { IMovieRequests } from "../../../../interfaces"; import { SearchV2Service } from "../../../../services/searchV2.service"; import { IMovieRatings } from "../../../../interfaces/IRatings"; import { APP_BASE_HREF } from "@angular/common"; +import { IStreamingData } from "../../../../interfaces/IStreams"; @Component({ templateUrl: "./movie-information-panel.component.html", styleUrls: ["../../../media-details.component.scss"], @@ -19,9 +20,12 @@ export class MovieInformationPanelComponent implements OnInit { @Input() public advancedOptions: boolean; public ratings: IMovieRatings; + public streams: IStreamingData[]; public ngOnInit() { this.searchService.getRottenMovieRatings(this.movie.title, +this.movie.releaseDate.toString().substring(0,4)) .subscribe(x => this.ratings = x); + + this.searchService.getMovieStreams(this.movie.id).subscribe(x => this.streams = x); } } diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.html b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.html index b7d8f6872..777f4820e 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.html @@ -5,7 +5,16 @@ {{ratings.score}}% - + +
+
+ {{'MediaDetails.StreamingOn' | translate }}: +
+ + + +
+

{{'MediaDetails.Status' | translate }}: diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.ts index 5217586f5..350f76c26 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.ts @@ -2,6 +2,7 @@ import { Component, ViewEncapsulation, Input, OnInit } from "@angular/core"; import { ITvRequests } from "../../../../../interfaces"; import { ITvRatings } from "../../../../../interfaces/IRatings"; import { ISearchTvResultV2 } from "../../../../../interfaces/ISearchTvResultV2"; +import { IStreamingData } from "../../../../../interfaces/IStreams"; import { SearchV2Service } from "../../../../../services"; @Component({ @@ -19,6 +20,7 @@ export class TvInformationPanelComponent implements OnInit { @Input() public advancedOptions: boolean; public ratings: ITvRatings; + public streams: IStreamingData[]; public seasonCount: number; public totalEpisodes: number = 0; public nextEpisode: any; @@ -26,6 +28,8 @@ export class TvInformationPanelComponent implements OnInit { public ngOnInit(): void { this.searchService.getRottenTvRatings(this.tv.title, +this.tv.firstAired.toString().substring(0,4)) .subscribe(x => this.ratings = x); + + this.searchService.getTvStreams(+this.tv.theTvDbId, this.tv.id).subscribe(x => this.streams = x); this.tv.seasonRequests.forEach(season => { this.totalEpisodes = this.totalEpisodes + season.episodes.length; }); diff --git a/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss b/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss index 7466e26ca..41dd1d87e 100644 --- a/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss +++ b/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss @@ -230,4 +230,10 @@ .rating-small { width: 1.3em; +} +.stream-small { + width: 3em; + border-radius: 1em; + margin-right: 10px; + margin-top: 5px; } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/services/identity.service.ts b/src/Ombi/ClientApp/src/app/services/identity.service.ts index f25a21b73..27f777e51 100644 --- a/src/Ombi/ClientApp/src/app/services/identity.service.ts +++ b/src/Ombi/ClientApp/src/app/services/identity.service.ts @@ -4,7 +4,7 @@ import { Injectable, Inject } from "@angular/core"; import { HttpClient } from "@angular/common/http"; import { Observable } from "rxjs"; -import { ICheckbox, ICreateWizardUser, IIdentityResult, INotificationPreferences, IResetPasswordToken, IUpdateLocalUser, IUser, IUserDropdown, IWizardUserResult } from "../interfaces"; +import { ICheckbox, ICreateWizardUser, IIdentityResult, INotificationPreferences, IResetPasswordToken, IStreamingCountries, IUpdateLocalUser, IUser, IUserDropdown, IWizardUserResult } from "../interfaces"; import { ServiceHelpers } from "./service.helpers"; @Injectable() @@ -83,8 +83,16 @@ export class IdentityService extends ServiceHelpers { public getNotificationPreferencesForUser(userId: string): Observable { return this.http.get(`${this.url}notificationpreferences/${userId}`, {headers: this.headers}); } - + public updateLanguage(lang: string): Observable { return this.http.post(`${this.url}language`, {lang: lang}, {headers: this.headers}); } + + public getSupportedStreamingCountries(): Observable { + return this.http.get(`${this.url}streamingcountry`, {headers: this.headers}); + } + + public updateStreamingCountry(code: string): Observable { + return this.http.post(`${this.url}streamingcountry`, {code: code}, {headers: this.headers}); + } } diff --git a/src/Ombi/ClientApp/src/app/services/searchV2.service.ts b/src/Ombi/ClientApp/src/app/services/searchV2.service.ts index 07a846185..fa970313f 100644 --- a/src/Ombi/ClientApp/src/app/services/searchV2.service.ts +++ b/src/Ombi/ClientApp/src/app/services/searchV2.service.ts @@ -12,6 +12,7 @@ import { ISearchTvResultV2, IMovieCollectionsViewModel, IActorCredits } from ".. import { IArtistSearchResult, IAlbumArt } from "../interfaces/IMusicSearchResultV2"; import { SearchFilter } from "../my-nav/SearchFilter"; import { IMovieRatings, ITvRatings } from "../interfaces/IRatings"; +import { IStreamingData } from "../interfaces/IStreams"; @Injectable() export class SearchV2Service extends ServiceHelpers { @@ -131,4 +132,12 @@ export class SearchV2Service extends ServiceHelpers { return this.http.get(`${this.url}/ratings/tv/${name}/${year}`); } + public getMovieStreams(theMovieDbId: number): Observable { + return this.http.get(`${this.url}/stream/movie/${theMovieDbId}`); + } + + public getTvStreams(theTvDbId: number, tvMaze: number): Observable { + return this.http.get(`${this.url}/stream/tv/${theTvDbId}/${tvMaze}`); + } + } diff --git a/src/Ombi/ClientApp/src/app/shared/shared.module.ts b/src/Ombi/ClientApp/src/app/shared/shared.module.ts index 543027bfd..ed93fa375 100644 --- a/src/Ombi/ClientApp/src/app/shared/shared.module.ts +++ b/src/Ombi/ClientApp/src/app/shared/shared.module.ts @@ -75,7 +75,7 @@ import { EpisodeRequestComponent } from "./episode-request/episode-request.compo MatSnackBarModule, ], entryComponents: [ - EpisodeRequestComponent + EpisodeRequestComponent, ], exports: [ TranslateModule, diff --git a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html index 9971775eb..402c683f1 100644 --- a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html +++ b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html @@ -3,24 +3,50 @@
-
-
+
+
+
+ {{'UserPreferences.LanguageDescription' | translate}} +
+ + + + + {{lang.display}} + + + +
- - - - - {{lang.display}} - - - -
-
-
+
-
+
+
+ +
+ +
+
+ {{'UserPreferences.StreamingCountryDescription' | translate}} +
+ + + + + {{value}} + + + +
+
+ +
+
+ + +
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.ts b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.ts index 3fe4359eb..3e4163ea0 100644 --- a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.ts +++ b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.ts @@ -3,7 +3,7 @@ import { AuthService } from "../../../auth/auth.service"; import { TranslateService } from "@ngx-translate/core"; import { AvailableLanguages, ILanguage } from "./user-preference.constants"; import { StorageService } from "../../../shared/storage/storage-service"; -import { IdentityService, SettingsService } from "../../../services"; +import { IdentityService, NotificationService, SettingsService } from "../../../services"; import { IUser } from "../../../interfaces"; @Component({ @@ -17,12 +17,14 @@ export class UserPreferenceComponent implements OnInit { public availableLanguages = AvailableLanguages; public qrCode: string; public qrCodeEnabled: boolean; + public countries: string[]; + public selectedCountry: string; private user: IUser; constructor(private authService: AuthService, private readonly translate: TranslateService, - private storage: StorageService, + private readonly notification: NotificationService, private readonly identityService: IdentityService, private readonly settingsService: SettingsService) { } @@ -33,6 +35,8 @@ export class UserPreferenceComponent implements OnInit { } const customization = await this.settingsService.getCustomization().toPromise(); + this.selectedLang = this.translate.currentLang; + const accessToken = await this.identityService.getAccessToken().toPromise(); this.qrCode = `${customization.applicationUrl}|${accessToken}`; @@ -43,14 +47,18 @@ export class UserPreferenceComponent implements OnInit { } this.user = await this.identityService.getUser().toPromise(); - if (this.user.language) { - this.selectedLang = this.user.language; - } + this.selectedCountry = this.user.streamingCountry; + this.identityService.getSupportedStreamingCountries().subscribe(x => this.countries = x); + } public languageSelected() { - this.identityService.updateLanguage(this.selectedLang).subscribe(); + this.identityService.updateLanguage(this.selectedLang).subscribe(x => this.notification.success(this.translate.instant("UserPreferences.Updated"))); this.translate.use(this.selectedLang); } + public countrySelected() { + this.identityService.updateStreamingCountry(this.selectedCountry).subscribe(x => this.notification.success(this.translate.instant("UserPreferences.Updated"))); + } + } diff --git a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement-user.component.html b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement-user.component.html index d03bb9d2e..64f5d76d2 100644 --- a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement-user.component.html +++ b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement-user.component.html @@ -18,6 +18,14 @@
+ + + + + {{value}} + + +
diff --git a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement-user.component.ts b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement-user.component.ts index db0403a01..486f456d0 100644 --- a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement-user.component.ts +++ b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement-user.component.ts @@ -1,5 +1,5 @@ import { Location } from "@angular/common"; -import { Component, OnInit } from "@angular/core"; +import { AfterViewInit, Component, OnInit } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; import { ICheckbox, INotificationAgent, INotificationPreferences, IRadarrProfile, IRadarrRootFolder, ISonarrProfile, ISonarrRootFolder, IUser, UserType } from "../interfaces"; @@ -25,6 +25,8 @@ export class UserManagementUserComponent implements OnInit { public NotificationAgent = INotificationAgent; public edit: boolean; + public countries: string[]; + constructor(private identityService: IdentityService, private notificationService: MessageService, private router: Router, @@ -45,6 +47,8 @@ export class UserManagementUserComponent implements OnInit { } public ngOnInit() { + + this.identityService.getSupportedStreamingCountries().subscribe(x => this.countries = x); this.identityService.getAllAvailableClaims().subscribe(x => this.availableClaims = x); if(this.edit) { this.identityService.getNotificationPreferencesForUser(this.userId).subscribe(x => this.notificationPreferences = x); @@ -74,6 +78,7 @@ export class UserManagementUserComponent implements OnInit { episodeRequestQuota: null, movieRequestQuota: null, language: null, + streamingCountry: "US", userQualityProfiles: { radarrQualityProfile: 0, radarrRootPath: 0, @@ -172,7 +177,7 @@ export class UserManagementUserComponent implements OnInit { } }); } - + public back() { this.location.back(); } diff --git a/src/Ombi/Controllers/V1/IdentityController.cs b/src/Ombi/Controllers/V1/IdentityController.cs index 2300c0482..500cd4610 100644 --- a/src/Ombi/Controllers/V1/IdentityController.cs +++ b/src/Ombi/Controllers/V1/IdentityController.cs @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Api.Plex; +using Ombi.Api.TheMovieDb.Models; using Ombi.Attributes; using Ombi.Core.Authentication; using Ombi.Core.Engine; @@ -159,7 +160,8 @@ namespace Ombi.Controllers.V1 UserName = plexUser.user.username, UserType = UserType.PlexUser, Email = plexUser.user.email, - ProviderUserId = plexUser.user.id + ProviderUserId = plexUser.user.id, + StreamingCountry = "US" // Default }; await _userManagementSettings.SaveSettingsAsync(new UserManagementSettings @@ -173,7 +175,8 @@ namespace Ombi.Controllers.V1 var userToCreate = new OmbiUser { UserName = user.Username, - UserType = UserType.LocalUser + UserType = UserType.LocalUser, + StreamingCountry = "US" }; return await SaveWizardUser(user, userToCreate); @@ -329,6 +332,32 @@ namespace Ombi.Controllers.V1 return Ok(); } + /// + /// Returns the supported country codes that we have streaming data for + /// + [HttpGet("streamingcountry")] + [Authorize] + public IActionResult GetSupportedStreamingCountries() + { + var resultsProp = typeof(Results).GetProperties(); + return Json(resultsProp.Select(x => x.Name)); + } + + /// + /// Sets the current users country streaming preference + /// + [HttpPost("streamingcountry")] + [Authorize] + public async Task SetCurrentUserCountryStreaming([FromBody] CountryStreamingPreference model) + { + var username = User.Identity.Name.ToUpper(); + var user = await UserManager.Users.FirstOrDefaultAsync(x => x.NormalizedUserName == username); + user.StreamingCountry = model.Code; + + await UserManager.UpdateAsync(user); + return Ok(); + } + /// /// Gets the user by the user id. /// @@ -358,7 +387,8 @@ namespace Ombi.Controllers.V1 EpisodeRequestLimit = user.EpisodeRequestLimit ?? 0, MovieRequestLimit = user.MovieRequestLimit ?? 0, MusicRequestLimit = user.MusicRequestLimit ?? 0, - Language = user.Language + Language = user.Language, + StreamingCountry = user.StreamingCountry }; foreach (var role in userRoles) @@ -437,6 +467,7 @@ namespace Ombi.Controllers.V1 EpisodeRequestLimit = user.EpisodeRequestLimit, MusicRequestLimit = user.MusicRequestLimit, UserAccessToken = Guid.NewGuid().ToString("N"), + StreamingCountry = user.StreamingCountry.HasValue() ? user.StreamingCountry : "US" }; var userResult = await UserManager.CreateAsync(ombiUser, user.Password); @@ -594,6 +625,10 @@ namespace Ombi.Controllers.V1 user.MovieRequestLimit = ui.MovieRequestLimit; user.EpisodeRequestLimit = ui.EpisodeRequestLimit; user.MusicRequestLimit = ui.MusicRequestLimit; + if (ui.StreamingCountry.HasValue()) + { + user.StreamingCountry = ui.StreamingCountry; + } var updateResult = await UserManager.UpdateAsync(user); if (!updateResult.Succeeded) { @@ -739,7 +774,7 @@ namespace Ombi.Controllers.V1 [HttpPost("reset")] [AllowAnonymous] [ApiExplorerSettings(IgnoreApi = true)] - public async Task SubmitResetPassword([FromBody]SubmitPasswordReset email) + public async Task SubmitResetPassword([FromBody] SubmitPasswordReset email) { // Check if account exists var user = await UserManager.FindByEmailAsync(email.Email); @@ -817,7 +852,7 @@ namespace Ombi.Controllers.V1 [HttpPost("resetpassword")] [AllowAnonymous] [ApiExplorerSettings(IgnoreApi = true)] - public async Task ResetPassword([FromBody]ResetPasswordToken token) + public async Task ResetPassword([FromBody] ResetPasswordToken token) { var user = await UserManager.FindByEmailAsync(token.Email); diff --git a/src/Ombi/Controllers/V2/SearchController.cs b/src/Ombi/Controllers/V2/SearchController.cs index af1378ee5..889dc288f 100644 --- a/src/Ombi/Controllers/V2/SearchController.cs +++ b/src/Ombi/Controllers/V2/SearchController.cs @@ -420,5 +420,20 @@ namespace Ombi.Controllers.V2 return _rottenTomatoesApi.GetTvRatings(name, year); } + [HttpGet("stream/movie/{movieDbId}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesDefaultResponseType] + public Task> GetMovieStreams(int movieDBId) + { + return _movieEngineV2.GetStreamInformation(movieDBId, HttpContext.RequestAborted); + } + + [HttpGet("stream/tv/{tvdbId}/{tvMaze}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesDefaultResponseType] + public Task> GetTvStreams(int tvdbId, int tvMaze) + { + return _tvEngineV2.GetStreamInformation(tvdbId, tvMaze, HttpContext.RequestAborted); + } } } \ No newline at end of file diff --git a/src/Ombi/Models/Identity/CountryStreamingPreference.cs b/src/Ombi/Models/Identity/CountryStreamingPreference.cs new file mode 100644 index 000000000..c2cc28798 --- /dev/null +++ b/src/Ombi/Models/Identity/CountryStreamingPreference.cs @@ -0,0 +1,7 @@ +namespace Ombi.Models.Identity +{ + public class CountryStreamingPreference + { + public string Code { get; set; } + } +} diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index ff1985637..d86cf652c 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -275,7 +275,8 @@ "SonarrConfiguration": "Sonarr Configuration", "RadarrConfiguration": "Radarr Configuration", "RequestOnBehalf": "Request on behalf of", - "PleaseSelectUser": "Please select a user" + "PleaseSelectUser": "Please select a user", + "StreamingOn": "Streaming On" }, "Discovery": { "PopularTab": "Popular", @@ -300,6 +301,11 @@ "UserPreferences": { "Welcome": "Welcome {{username}}!", "OmbiLanguage": "Language", - "DarkMode": "Dark Mode" + "DarkMode": "Dark Mode", + "Updated": "Successfully Updated", + "StreamingCountry":"Streaming Country", + "StreamingCountryDescription": "This is the country code that we will display streaming information for. If you are in the US please select US and you will have US related streaming information.", + "LanguageDescription": "This is the language you would like the Ombi interface to be displayed in.", + "MobileQRCode":"Mobile QR Code" } } \ No newline at end of file From de886dd80079fead9463ab55118b504d5835bba0 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Thu, 7 Jan 2021 20:14:13 +0000 Subject: [PATCH 67/82] Fixed the issue with the null streaming country --- src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs | 3 ++- src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs | 3 ++- src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs | 6 ++++-- src/Ombi.Settings/Settings/Models/UserManagementSettings.cs | 1 + src/Ombi.Store/Context/OmbiContext.cs | 1 + 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs index 13821f5d9..4b684b8ab 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyUserImporter.cs @@ -117,7 +117,8 @@ namespace Ombi.Schedule.Jobs.Emby ProviderUserId = embyUser.Id, Alias = isConnectUser ? embyUser.Name : string.Empty, MovieRequestLimit = userManagementSettings.MovieRequestLimit, - EpisodeRequestLimit = userManagementSettings.EpisodeRequestLimit + EpisodeRequestLimit = userManagementSettings.EpisodeRequestLimit, + StreamingCountry = userManagementSettings.DefaultStreamingCountry }; var result = await _userManager.CreateAsync(newUser); if (!result.Succeeded) diff --git a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs index 790052309..15be41ec7 100644 --- a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs +++ b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs @@ -115,7 +115,8 @@ namespace Ombi.Schedule.Jobs.Jellyfin UserType = UserType.JellyfinUser, ProviderUserId = jellyfinUser.Id, MovieRequestLimit = userManagementSettings.MovieRequestLimit, - EpisodeRequestLimit = userManagementSettings.EpisodeRequestLimit + EpisodeRequestLimit = userManagementSettings.EpisodeRequestLimit, + StreamingCountry = userManagementSettings.DefaultStreamingCountry; }; _log.LogInformation("Creating Jellyfin user {0}", newUser.UserName); var result = await _userManager.CreateAsync(newUser); diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs b/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs index c80703f81..05ba3194f 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs @@ -98,7 +98,8 @@ namespace Ombi.Schedule.Jobs.Plex Email = plexUser?.Email ?? string.Empty, Alias = string.Empty, MovieRequestLimit = userManagementSettings.MovieRequestLimit, - EpisodeRequestLimit = userManagementSettings.EpisodeRequestLimit + EpisodeRequestLimit = userManagementSettings.EpisodeRequestLimit, + StreamingCountry = userManagementSettings.DefaultStreamingCountry }; _log.LogInformation("Creating Plex user {0}", newUser.UserName); var result = await _userManager.CreateAsync(newUser); @@ -161,7 +162,8 @@ namespace Ombi.Schedule.Jobs.Plex UserName = plexAdmin.username ?? plexAdmin.id, ProviderUserId = plexAdmin.id, Email = plexAdmin.email ?? string.Empty, - Alias = string.Empty + Alias = string.Empty, + StreamingCountry = settings.DefaultStreamingCountry; }; var result = await _userManager.CreateAsync(newUser); diff --git a/src/Ombi.Settings/Settings/Models/UserManagementSettings.cs b/src/Ombi.Settings/Settings/Models/UserManagementSettings.cs index 7d0271722..ff6cff278 100644 --- a/src/Ombi.Settings/Settings/Models/UserManagementSettings.cs +++ b/src/Ombi.Settings/Settings/Models/UserManagementSettings.cs @@ -10,6 +10,7 @@ namespace Ombi.Settings.Settings.Models public bool ImportJellyfinUsers { get; set; } public int MovieRequestLimit { get; set; } public int EpisodeRequestLimit { get; set; } + public string DefaultStreamingCountry { get; set; } = "US"; public List DefaultRoles { get; set; } = new List(); public List BannedPlexUserIds { get; set; } = new List(); public List BannedEmbyUserIds { get; set; } = new List(); diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index 90e559875..490f352a6 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -69,6 +69,7 @@ namespace Ombi.Store.Context UserName = "Api", UserType = UserType.SystemUser, NormalizedUserName = "API", + StreamingCountry = "US" }); SaveChanges(); tran.Commit(); From f54ac1c40154ba869077a58e498bd9a085e055dc Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Fri, 8 Jan 2021 08:36:58 +0000 Subject: [PATCH 68/82] fixed build --- src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs | 2 +- src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs index 15be41ec7..96973580d 100644 --- a/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs +++ b/src/Ombi.Schedule/Jobs/Jellyfin/JellyfinUserImporter.cs @@ -116,7 +116,7 @@ namespace Ombi.Schedule.Jobs.Jellyfin ProviderUserId = jellyfinUser.Id, MovieRequestLimit = userManagementSettings.MovieRequestLimit, EpisodeRequestLimit = userManagementSettings.EpisodeRequestLimit, - StreamingCountry = userManagementSettings.DefaultStreamingCountry; + StreamingCountry = userManagementSettings.DefaultStreamingCountry }; _log.LogInformation("Creating Jellyfin user {0}", newUser.UserName); var result = await _userManager.CreateAsync(newUser); diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs b/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs index 05ba3194f..0ac9d33c4 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs @@ -163,7 +163,7 @@ namespace Ombi.Schedule.Jobs.Plex ProviderUserId = plexAdmin.id, Email = plexAdmin.email ?? string.Empty, Alias = string.Empty, - StreamingCountry = settings.DefaultStreamingCountry; + StreamingCountry = settings.DefaultStreamingCountry }; var result = await _userManager.CreateAsync(newUser); From bb29b547b3d925e338ef023a2f275d02dfc68098 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Fri, 8 Jan 2021 19:16:52 +0000 Subject: [PATCH 69/82] * Fixed not showing the view on X button for TV shows * Fixed the banners for Partially available TV shows --- src/Ombi/.vscode/launch.json | 11 +++++++++++ .../components/card/discover-card.component.html | 1 - .../components/card/discover-card.component.ts | 15 +-------------- .../components/discover/discover.component.ts | 2 +- .../components/tv/tv-details.component.html | 2 +- 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/Ombi/.vscode/launch.json b/src/Ombi/.vscode/launch.json index 0a45aaea0..b14ee6004 100644 --- a/src/Ombi/.vscode/launch.json +++ b/src/Ombi/.vscode/launch.json @@ -1,6 +1,17 @@ { "version": "0.2.0", "configurations": [ + { + "name": "Launch Backend", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/bin/Debug/net5.0/ombi.dll", + "args": ["--host", "http://localhost:3577"], + "cwd": "${workspaceFolder}", + "stopAtEntry": false, + "console": "internalConsole" + }, { "name": "ng serve", "type": "chrome", diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html index ee3e59783..faec960b1 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html @@ -1,7 +1,6 @@
- {{getAvailbilityStatus()}}
diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts index 335e88649..ec3dd9986 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts @@ -52,19 +52,6 @@ export class DiscoverCardComponent implements OnInit { return ""; } - public getAvailbility(): string { - if (this.result.available) { - return "fa-check-circle"; - } - if (this.result.approved) { - return "fa-check-circle"; - } - if (this.result.requested) { - return "fa-question-circle"; - } - return ""; - } - public getAvailbilityStatus(): string { if (this.result.available) { return "Available"; @@ -107,7 +94,7 @@ export class DiscoverCardComponent implements OnInit { private updateTvItem(updated: ISearchTvResultV2) { this.result.title = updated.title; this.result.id = updated.id; - this.result.available = updated.fullyAvailable; + this.result.available = updated.fullyAvailable || updated.partlyAvailable; this.result.posterPath = updated.banner; this.result.requested = updated.requested; this.result.url = updated.imdbId; diff --git a/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.ts b/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.ts index c42a0417d..c1ed3bc63 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.ts @@ -290,7 +290,7 @@ export class DiscoverComponent implements OnInit { url: undefined, rating: +m.rating, overview: m.overview, - approved: m.approved, + approved: m.approved || m.partlyAvailable, imdbid: m.imdbId, denied: false, background: m.background diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.html b/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.html index 70832f267..1dfa5c544 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.html @@ -28,7 +28,7 @@
From c26f3337edce4167f82fd7e0cb622355a41869a3 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Fri, 8 Jan 2021 19:27:24 +0000 Subject: [PATCH 70/82] Stopped #3961 from happening --- src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs | 6 ++++++ src/Ombi/Controllers/V1/IdentityController.cs | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs b/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs index 0ac9d33c4..1e484b237 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs @@ -88,6 +88,12 @@ namespace Ombi.Schedule.Jobs.Plex _log.LogInformation("Could not create Plex user since the have no username, PlexUserId: {0}", plexUser.Id); continue; } + + if ((plexUser.Email.HasValue()) && await _userManager.FindByEmailAsync(plexUser.Email) != null) + { + _log.LogWarning($"Cannot add user {plexUser.Username} because their email address is already in Ombi, skipping this user"); + continue; + } // Create this users // We do not store a password against the user since they will authenticate via Plex var newUser = new OmbiUser diff --git a/src/Ombi/Controllers/V1/IdentityController.cs b/src/Ombi/Controllers/V1/IdentityController.cs index 500cd4610..63f8211ca 100644 --- a/src/Ombi/Controllers/V1/IdentityController.cs +++ b/src/Ombi/Controllers/V1/IdentityController.cs @@ -457,6 +457,10 @@ namespace Ombi.Controllers.V1 { return Error("You do not have the correct permissions to create this user"); } + if(user.EmailAddress.HasValue() && await UserManager.FindByEmailAsync(user.EmailAddress) != null) + { + return Error("This email has already been taken"); + } var ombiUser = new OmbiUser { Alias = user.Alias, From b4b80165b9b494f35509daa3d08f2335e178e480 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Fri, 8 Jan 2021 19:29:28 +0000 Subject: [PATCH 71/82] * Fixed #3952 where the metadata update would fail --- src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index a3db357af..cbc2e6287 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -420,6 +420,10 @@ namespace Ombi.Schedule.Jobs.Ombi continue; } var entity = await _plex.Find(movie.Id); + if (entity == null) + { + return; + } entity.TheMovieDbId = movie.TheMovieDbId; _plex.UpdateWithoutSave(entity); } From 0936354df57ecddd4d0b6d33139b3c0fd974e14c Mon Sep 17 00:00:00 2001 From: tidusjar Date: Fri, 8 Jan 2021 19:55:44 +0000 Subject: [PATCH 72/82] * Fixed not being able to remove legacy mobile users #3968 --- src/Ombi/.vscode/launch.json | 12 ++++++++++++ .../ClientApp/src/app/services/mobile.service.ts | 2 +- src/Ombi/Controllers/V1/MobileController.cs | 8 ++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/Ombi/.vscode/launch.json b/src/Ombi/.vscode/launch.json index b14ee6004..642e584c5 100644 --- a/src/Ombi/.vscode/launch.json +++ b/src/Ombi/.vscode/launch.json @@ -12,6 +12,18 @@ "stopAtEntry": false, "console": "internalConsole" }, + { + "name": "Launch Frontend", + "type": "pwa-node", + "request": "launch", + "runtimeExecutable": "yarn", + "cwd": "${workspaceFolder}/ClientApp", + "runtimeArgs": ["start"], + "resolveSourceMapLocations": [ + "${workspaceFolder}/**", + "!**/node_modules/**" + ], + }, { "name": "ng serve", "type": "chrome", diff --git a/src/Ombi/ClientApp/src/app/services/mobile.service.ts b/src/Ombi/ClientApp/src/app/services/mobile.service.ts index 177f1f35f..9a471ee47 100644 --- a/src/Ombi/ClientApp/src/app/services/mobile.service.ts +++ b/src/Ombi/ClientApp/src/app/services/mobile.service.ts @@ -17,6 +17,6 @@ export class MobileService extends ServiceHelpers { } public deleteUser(userId: string): Observable { - return this.http.post(`${this.url}remove/`, userId, {headers: this.headers}); + return this.http.post(`${this.url}`, { userId: userId }, {headers: this.headers}); } } diff --git a/src/Ombi/Controllers/V1/MobileController.cs b/src/Ombi/Controllers/V1/MobileController.cs index 2bae31c9d..2ca640769 100644 --- a/src/Ombi/Controllers/V1/MobileController.cs +++ b/src/Ombi/Controllers/V1/MobileController.cs @@ -83,12 +83,16 @@ namespace Ombi.Controllers.V1 return vm; } + public class RemoveUserModel + { + public string UserId { get; set; } + } [HttpPost] [ApiExplorerSettings(IgnoreApi = true)] [Admin] - public async Task RemoveUser([FromBody] string userId) + public async Task RemoveUser([FromBody] RemoveUserModel userId) { - var user = await _userManager.Users.Include(x => x.NotificationUserIds).FirstOrDefaultAsync(x => x.Id.Equals(userId, StringComparison.InvariantCultureIgnoreCase)); + var user = await _userManager.Users.Include(x => x.NotificationUserIds).FirstOrDefaultAsync(x => x.Id.Equals(userId.UserId, StringComparison.InvariantCultureIgnoreCase)); try { await _notification.DeleteRange(user.NotificationUserIds); From 93997116be8c0927c32e32705d4d051014d901c3 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Fri, 8 Jan 2021 20:00:01 +0000 Subject: [PATCH 73/82] * Added the default streaming country to the user importer --- .../ClientApp/src/app/interfaces/ISettings.ts | 1 + .../usermanagement.component.html | 17 +++++++++++++---- .../usermanagement/usermanagement.component.ts | 2 ++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts index 744148ff9..c5b29adff 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts @@ -212,6 +212,7 @@ export interface IUserManagementSettings extends ISettings { bannedPlexUserIds: string[]; bannedEmbyUserIds: string[]; bannedJellyfinUserIds: string[]; + defaultStreamingCountry: string; } export interface IAbout { diff --git a/src/Ombi/ClientApp/src/app/settings/usermanagement/usermanagement.component.html b/src/Ombi/ClientApp/src/app/settings/usermanagement/usermanagement.component.html index bd259aff6..ba5ae4842 100644 --- a/src/Ombi/ClientApp/src/app/settings/usermanagement/usermanagement.component.html +++ b/src/Ombi/ClientApp/src/app/settings/usermanagement/usermanagement.component.html @@ -13,13 +13,13 @@ Import Plex Users
- +
Import Plex Admin

Plex Users excluded from Import

- +
@@ -27,10 +27,10 @@
Import Emby Users
- +

Emby Users excluded from Import

- +
@@ -75,6 +75,15 @@
+ + + + + {{value}} + + + +
diff --git a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.ts b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.ts index b412091a8..66268082b 100644 --- a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.ts +++ b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.ts @@ -27,8 +27,11 @@ export class UserManagementComponent implements OnInit { public bulkMovieLimit?: number; public bulkEpisodeLimit?: number; public bulkMusicLimit?: number; + public bulkStreaming?: string; public plexEnabled: boolean; + public countries: string[]; + constructor(private identityService: IdentityService, private settingsService: SettingsService, private notificationService: NotificationService, @@ -38,6 +41,7 @@ export class UserManagementComponent implements OnInit { public async ngOnInit() { + this.identityService.getSupportedStreamingCountries().subscribe(x => this.countries = x); this.users = await this.identityService.getUsers().toPromise(); this.dataSource = new MatTableDataSource(this.users); this.dataSource.sort = this.sort; From 2ca6d6757384534d3b1a4bbad7d47b024edd011e Mon Sep 17 00:00:00 2001 From: tidusjar Date: Fri, 8 Jan 2021 20:33:44 +0000 Subject: [PATCH 75/82] * Made the "Play on Plex/Emby/Jellyfin" buttons more visible on the details page --- .../social-icons/social-icons.component.html | 23 +++++++++++-------- .../social-icons/social-icons.component.scss | 22 +++++++++++------- .../media-details.component.scss | 3 +-- src/Ombi/wwwroot/translations/en.json | 6 ++--- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html index 68fff403d..934423512 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html @@ -30,19 +30,22 @@ - - + + + {{'Search.ViewOnPlex' | translate}} + - - + + {{'Search.ViewOnEmby' | translate}} + - - + + {{'Search.ViewOnJellyfin' | translate}} + - diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.scss b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.scss index 67d621a33..24e22e7b7 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.scss +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.scss @@ -1,13 +1,19 @@ - -.media-icons.plex { - color: #feb801 !important; +.viewon-btn { + background-color: transparent; + margin-top: 7px; + text-decoration: none; } -.media-icons.emby { - color: #52b54a !important; +.viewon-btn.plex { + border: 1px solid #ffd740; + color: #ffd740; } - -.media-icons.jellyfin { - color: #00a4dc !important; +.viewon-btn.emby { + border: 1px solid #52b54a; + color: #52b54a; } +.viewon-btn.jellyfin { + border: 1px solid #00a4dc; + color: #00a4dc; +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss b/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss index 41dd1d87e..290c60fd2 100644 --- a/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss +++ b/src/Ombi/ClientApp/src/app/media-details/media-details.component.scss @@ -185,7 +185,7 @@ } .media-icons { - color: mat-color($ombi-app-primary) !important; + color: white !important; padding: 1%; } @@ -200,7 +200,6 @@ - .small-middle-container { margin: auto; width: 95%; diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index d86cf652c..015a65a00 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -90,9 +90,9 @@ "NoResults": "Sorry, we didn't find any results!", "DigitalDate": "Digital Release: {{date}}", "TheatricalRelease": "Theatrical Release: {{date}}", - "ViewOnPlex": "View On Plex", - "ViewOnEmby": "View On Emby", - "ViewOnJellyfin": "View On Jellyfin", + "ViewOnPlex": "Play On Plex", + "ViewOnEmby": "Play On Emby", + "ViewOnJellyfin": "Play On Jellyfin", "RequestAdded": "Request for {{title}} has been added successfully", "Similar": "Similar", "Refine": "Refine", From 74db3cd573bea522c01f096788e40662291aa3ba Mon Sep 17 00:00:00 2001 From: tidusjar Date: Fri, 8 Jan 2021 21:05:19 +0000 Subject: [PATCH 76/82] * Improved the responsiveness for mobile views on the detail pages --- .../components/movie/movie-details.component.html | 4 ++-- .../shared/top-banner/top-banner.component.html | 6 +++--- .../shared/top-banner/top-banner.component.scss | 3 +++ .../components/shared/top-banner/top-banner.component.ts | 1 + .../components/tv/tv-details.component.html | 4 ++-- .../src/app/media-details/media-details.component.scss | 9 +++------ src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts | 2 +- 7 files changed, 15 insertions(+), 14 deletions(-) create mode 100644 src/Ombi/ClientApp/src/app/media-details/components/shared/top-banner/top-banner.component.scss diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html index 2e2f4e1d1..e3fd88067 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html @@ -9,12 +9,12 @@
-
+
-
+
diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/top-banner/top-banner.component.html b/src/Ombi/ClientApp/src/app/media-details/components/shared/top-banner/top-banner.component.html index 7d88fa63f..bdb9aac37 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/top-banner/top-banner.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/top-banner/top-banner.component.html @@ -7,12 +7,12 @@
-

{{title}} + class="col-xl-11 col-lg-8 offset-xl-1 offset-lg-4 col-md-8 col-sm-7 col-12 offset-md-4 offset-sm-5 mobile-top-text"> +

{{title}} ({{releaseDate | amLocal | amDateFormat: 'YYYY'}})

-
{{tagline}}
+

{{tagline}}

diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/top-banner/top-banner.component.scss b/src/Ombi/ClientApp/src/app/media-details/components/shared/top-banner/top-banner.component.scss new file mode 100644 index 000000000..7ad12a274 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/top-banner/top-banner.component.scss @@ -0,0 +1,3 @@ +.large-text { + font: 500 40px/30px Roboto, "Helvetica Neue", sans-serif; +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/top-banner/top-banner.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/shared/top-banner/top-banner.component.ts index 583376537..ae61e3f3e 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/top-banner/top-banner.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/top-banner/top-banner.component.ts @@ -4,6 +4,7 @@ import { DomSanitizer, SafeStyle } from "@angular/platform-browser"; @Component({ selector: "top-banner", templateUrl: "./top-banner.component.html", + styleUrls: ["top-banner.component.scss"] }) export class TopBannerComponent { diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.html b/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.html index 1dfa5c544..a479aba81 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.html @@ -19,13 +19,13 @@
-
+
-
+
= this.breakpointObserver.observe(Breakpoints.Handset) + isHandset$: Observable = this.breakpointObserver.observe([Breakpoints.Small, Breakpoints.Handset, Breakpoints.XSmall]) .pipe( map(result => result.matches) ); From 39680792e9ad7cf81accb76054e3a3954fb57fa1 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Sat, 9 Jan 2021 22:41:13 +0000 Subject: [PATCH 77/82] Added the new search, when searching it now will display the results similar to the discover page * Also made minor improvements to the banners displayed for TV shows --- src/Ombi/ClientApp/package.json | 1 + src/Ombi/ClientApp/src/app/app.module.ts | 2 + .../card/discover-card.component.html | 4 +- .../card/discover-card.component.scss | 1 - .../card/discover-card.component.ts | 21 ++- .../src/app/discover/components/index.ts | 5 +- .../search-results.component.html | 10 ++ .../search-results.component.scss | 19 +++ .../search-results.component.ts | 143 ++++++++++++++++++ .../ClientApp/src/app/discover/interfaces.ts | 2 + .../app/discover/services/filter-service.ts | 14 ++ .../movie/movie-details.component.ts | 22 ++- .../src/app/my-nav/my-nav.component.html | 2 +- .../src/app/my-nav/my-nav.component.ts | 6 +- .../src/app/my-nav/nav-search.component.ts | 13 +- src/Ombi/ClientApp/yarn.lock | 5 + 16 files changed, 245 insertions(+), 25 deletions(-) create mode 100644 src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html create mode 100644 src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.scss create mode 100644 src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts create mode 100644 src/Ombi/ClientApp/src/app/discover/services/filter-service.ts diff --git a/src/Ombi/ClientApp/package.json b/src/Ombi/ClientApp/package.json index 988d20daf..82c946672 100644 --- a/src/Ombi/ClientApp/package.json +++ b/src/Ombi/ClientApp/package.json @@ -42,6 +42,7 @@ "font-awesome": "^4.7.0", "fullcalendar": "^4.0.0-alpha.4", "jquery": "3.3.1", + "lodash": "^4.17.20", "moment": "^2.23.0", "ng2-cookies": "^1.0.12", "ngx-clipboard": "^12.1.0", diff --git a/src/Ombi/ClientApp/src/app/app.module.ts b/src/Ombi/ClientApp/src/app/app.module.ts index 85d6f7a70..cfaf783cf 100644 --- a/src/Ombi/ClientApp/src/app/app.module.ts +++ b/src/Ombi/ClientApp/src/app/app.module.ts @@ -67,6 +67,7 @@ import { SignalRNotificationService } from "./services/signlarnotification.servi import { MatMenuModule } from "@angular/material/menu"; import { RemainingRequestsComponent } from "./shared/remaining-requests/remaining-requests.component"; import { UnauthorizedInterceptor } from "./auth/unauthorized.interceptor"; +import { FilterService } from "./discover/services/filter-service"; const routes: Routes = [ { path: "*", component: PageNotFoundComponent }, @@ -193,6 +194,7 @@ export function JwtTokenGetter() { MessageService, StorageService, RequestService, + FilterService, SignalRNotificationService, { provide: APP_BASE_HREF, diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html index faec960b1..57581bb32 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html @@ -1,5 +1,5 @@ -
- +
+
{{getAvailbilityStatus()}}
diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.scss b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.scss index 75bc29d72..5f232a48e 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.scss +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.scss @@ -73,7 +73,6 @@ small { .box { position: relative; max-width: 600px; - box-shadow: 0 0 15px rgba(0,0,0,.1); } /* common */ diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts index ec3dd9986..c923de72b 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts @@ -16,6 +16,7 @@ export class DiscoverCardComponent implements OnInit { @Input() public result: IDiscoverCardResult; public RequestType = RequestType; + public hide: boolean; constructor(private searchService: SearchV2Service, private dialog: MatDialog) { } @@ -33,7 +34,15 @@ export class DiscoverCardComponent implements OnInit { } public async getExtraTvInfo() { - var result = await this.searchService.getTvInfo(this.result.id); + if (this.result.tvMovieDb) { + var result = await this.searchService.getTvInfoWithMovieDbId(this.result.id); + } else { + var result = await this.searchService.getTvInfo(this.result.id); + } + if(result.status === "404") { + this.hide = true; + return; + } this.setTvDefaults(result); this.updateTvItem(result); @@ -80,14 +89,16 @@ export class DiscoverCardComponent implements OnInit { this.result.requested = updated.requested; this.result.requested = updated.requestProcessing; this.result.rating = updated.voteAverage; + this.result.overview = updated.overview; + this.result.imdbid = updated.imdbId; } private setTvDefaults(x: ISearchTvResultV2) { - if (!x.imdbId) { - x.imdbId = "https://www.tvmaze.com/shows/" + x.seriesId; - } else { + if (x.imdbId) { x.imdbId = "http://www.imdb.com/title/" + x.imdbId + "/"; + } else { + x.imdbId = "https://www.tvmaze.com/shows/" + x.seriesId; } } @@ -98,6 +109,8 @@ export class DiscoverCardComponent implements OnInit { this.result.posterPath = updated.banner; this.result.requested = updated.requested; this.result.url = updated.imdbId; + this.result.overview = updated.overview; + this.result.approved = updated.approved; } } diff --git a/src/Ombi/ClientApp/src/app/discover/components/index.ts b/src/Ombi/ClientApp/src/app/discover/components/index.ts index e65e48bd3..11ce07d50 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/index.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/index.ts @@ -8,6 +8,7 @@ import { AuthGuard } from "../../auth/auth.guard"; import { SearchService, RequestService } from "../../services"; import { MatDialog } from "@angular/material/dialog"; import { DiscoverGridComponent } from "./grid/discover-grid.component"; +import { DiscoverSearchResultsComponent } from "./search-results/search-results.component"; export const components: any[] = [ @@ -17,6 +18,7 @@ export const components: any[] = [ DiscoverCollectionsComponent, DiscoverActorComponent, DiscoverGridComponent, + DiscoverSearchResultsComponent, ]; @@ -33,5 +35,6 @@ export const providers: any[] = [ export const routes: Routes = [ { path: "", component: DiscoverComponent, canActivate: [AuthGuard] }, { path: "collection/:collectionId", component: DiscoverCollectionsComponent, canActivate: [AuthGuard] }, - { path: "actor/:actorId", component: DiscoverActorComponent, canActivate: [AuthGuard] } + { path: "actor/:actorId", component: DiscoverActorComponent, canActivate: [AuthGuard] }, + { path: ":searchTerm", component: DiscoverSearchResultsComponent, canActivate: [AuthGuard] }, ]; \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html new file mode 100644 index 000000000..e6c372b57 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html @@ -0,0 +1,10 @@ +
+
+ +
+
+
+ +
+
+
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.scss b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.scss new file mode 100644 index 000000000..728ff23c5 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.scss @@ -0,0 +1,19 @@ +.full-height { + height: 100%; +} + + +.small-middle-container{ + margin: auto; + width: 80%; +} + +.small-padding { + padding-left: 20px; + padding-right: 20px; + margin-bottom: 28px; +} + +.loading-spinner { + margin: 10%; +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts new file mode 100644 index 000000000..b43581af8 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts @@ -0,0 +1,143 @@ +import { Component, OnInit } from "@angular/core"; +import { ActivatedRoute } from "@angular/router"; +import { SearchV2Service } from "../../../services"; +import { IDiscoverCardResult } from "../../interfaces"; +import { IMultiSearchResult, RequestType } from "../../../interfaces"; +import { FilterService } from "../../services/filter-service"; +import { SearchFilter } from "../../../my-nav/SearchFilter"; +import { StorageService } from "../../../shared/storage/storage-service"; + +import { isEqual } from "lodash"; + +@Component({ + templateUrl: "./search-results.component.html", + styleUrls: ["./search-results.component.scss"], +}) +export class DiscoverSearchResultsComponent implements OnInit { + + public loadingFlag: boolean; + public searchTerm: string; + public results: IMultiSearchResult[]; + + public discoverResults: IDiscoverCardResult[] = []; + + public filter: SearchFilter; + + constructor(private searchService: SearchV2Service, + private route: ActivatedRoute, + private filterService: FilterService, + private store: StorageService) { + this.route.params.subscribe((params: any) => { + this.searchTerm = params.searchTerm; + this.clear(); + this.init(); + }); + } + + public async ngOnInit() { + this.loadingFlag = true; + + this.filterService.onFilterChange.subscribe(async x => { + if (!isEqual(this.filter, x)) { + this.filter = { ...x }; + await this.search(); + } + }); + } + + public async init() { + var filter = this.store.get("searchFilter"); + if (filter) { + this.filter = Object.assign(new SearchFilter(), JSON.parse(filter)); + } + this.loading(); + await this.search(); + } + + public createInitalModel() { + this.finishLoading(); + this.results.forEach(m => { + + let mediaType = RequestType.movie; + if (m.mediaType == "movie") { + mediaType = RequestType.movie; + } else if (m.mediaType == "tv") { + mediaType = RequestType.tvShow; + } else if (m.mediaType == "Artist") { + mediaType = RequestType.album; + } + + this.discoverResults.push({ + posterPath: `https://image.tmdb.org/t/p/w300/${m.poster}`, + requested: false, + title: m.title, + type: mediaType, + id: +m.id, + url: "", + rating: 0, + overview: "", + approved: false, + imdbid: "", + denied: false, + background: "", + available: false, + tvMovieDb: mediaType === RequestType.tvShow ? true : false + }); + + // switch (mediaType) { + // case RequestType.movie: + // this.searchService.getFullMovieDetails(+m.id) + // .subscribe(x => { + // const index = this.discoverResults.findIndex((obj => obj.id === +m.id)); + // this.discoverResults[index].available = x.available; + // this.discoverResults[index].requested = x.requested; + // this.discoverResults[index].requested = x.requested; + // this.discoverResults[index].requested = x.requested; + // this.discoverResults[index].requested = x.requested; + // this.discoverResults[index].requested = x.requested; + // }); + // } + }); + } + + // private createModel() { + // this.finishLoading(); + // this.collection.collection.forEach(m => { + // this.discoverResults.push({ + // available: m.available, + // posterPath: `https://image.tmdb.org/t/p/w300/${m.posterPath}`, + // requested: m.requested, + // title: m.title, + // type: RequestType.movie, + // id: m.id, + // url: `http://www.imdb.com/title/${m.imdbId}/`, + // rating: 0, + // overview: m.overview, + // approved: m.approved, + // imdbid: m.imdbId, + // denied:false, + // background: "" + // }); + // }); + // } + + private loading() { + this.loadingFlag = true; + } + + private finishLoading() { + this.loadingFlag = false; + } + + private clear() { + this.results = []; + this.discoverResults = []; + } + + private async search() { + this.clear(); + this.results = await this.searchService + .multiSearch(this.searchTerm, this.filter).toPromise(); + this.createInitalModel(); + } +} diff --git a/src/Ombi/ClientApp/src/app/discover/interfaces.ts b/src/Ombi/ClientApp/src/app/discover/interfaces.ts index 8a3276a2d..bac0faafc 100644 --- a/src/Ombi/ClientApp/src/app/discover/interfaces.ts +++ b/src/Ombi/ClientApp/src/app/discover/interfaces.ts @@ -14,6 +14,8 @@ export interface IDiscoverCardResult { overview: string; imdbid: string; background: string|any; + + tvMovieDb?: boolean; } export enum DiscoverOption { diff --git a/src/Ombi/ClientApp/src/app/discover/services/filter-service.ts b/src/Ombi/ClientApp/src/app/discover/services/filter-service.ts new file mode 100644 index 000000000..bceeb05d6 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/discover/services/filter-service.ts @@ -0,0 +1,14 @@ +import { EventEmitter, Injectable, Output } from "@angular/core"; +import { SearchFilter } from "../../my-nav/SearchFilter"; + +@Injectable() +export class FilterService { + + @Output() public onFilterChange = new EventEmitter(); + public filter: SearchFilter; + + public changeFilter(filter: SearchFilter) { + this.filter = filter; + this.onFilterChange.emit(this.filter); + } +} diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts index 7ffb79eee..bb02b0985 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts @@ -68,10 +68,7 @@ export class MovieDetailsComponent implements OnInit { this.hasRequest = true; this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId); } - this.imageService.getMovieBanner(this.theMovidDbId.toString()).subscribe(x => { - this.movie.background = this.sanitizer.bypassSecurityTrustStyle - ("url(" + x + ")"); - }); + this.loadBanner(); }); } else { this.searchService.getFullMovieDetails(this.theMovidDbId).subscribe(async x => { @@ -81,10 +78,7 @@ export class MovieDetailsComponent implements OnInit { this.hasRequest = true; this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId); } - this.imageService.getMovieBanner(this.theMovidDbId.toString()).subscribe(x => { - this.movie.background = this.sanitizer.bypassSecurityTrustStyle - ("url(" + x + ")"); - }); + this.loadBanner(); }); } } @@ -178,4 +172,16 @@ export class MovieDetailsComponent implements OnInit { } }); } + + private loadBanner() { + this.imageService.getMovieBanner(this.theMovidDbId.toString()).subscribe(x => { + if (!this.movie.backdropPath) { + this.movie.background = this.sanitizer.bypassSecurityTrustStyle + ("url(" + x + ")"); + } else { + this.movie.background = this.sanitizer.bypassSecurityTrustStyle + ("url(https://image.tmdb.org/t/p/original/" + this.movie.backdropPath + ")"); + } + }); + } } diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html index 849fc9f21..4a212ef8e 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html @@ -76,7 +76,7 @@
- +
diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts index 356047885..cafef32a8 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts @@ -9,6 +9,7 @@ import { MatSlideToggleChange } from '@angular/material/slide-toggle'; import { SearchFilter } from './SearchFilter'; import { Md5 } from 'ts-md5/dist/md5'; import { RequestType } from '../interfaces'; +import { FilterService } from '../discover/services/filter-service'; export enum SearchFilterType { Movie = 1, @@ -48,7 +49,8 @@ export class MyNavComponent implements OnInit { constructor(private breakpointObserver: BreakpointObserver, private settingsService: SettingsService, - private store: StorageService) { + private store: StorageService, + private filterService: FilterService) { } public async ngOnInit() { @@ -76,6 +78,7 @@ export class MyNavComponent implements OnInit { var filter = this.store.get("searchFilter"); if (filter) { this.searchFilter = Object.assign(new SearchFilter(), JSON.parse(filter)); + this.filterService.changeFilter(this.searchFilter); } this.navItems = [ { name: "NavigationBar.Discover", icon: "find_replace", link: "/discover", requiresAdmin: false, enabled: true, faIcon: null }, @@ -124,6 +127,7 @@ export class MyNavComponent implements OnInit { this.searchFilter.people = event.checked; break; } + this.filterService.changeFilter(this.searchFilter); this.store.save("searchFilter", JSON.stringify(this.searchFilter)); } diff --git a/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.ts b/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.ts index 9467f9544..4474aee4e 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.ts +++ b/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.ts @@ -7,12 +7,12 @@ import { } from "rxjs/operators"; import { empty} from "rxjs"; -import { SearchV2Service } from "../services/searchV2.service"; import { IMultiSearchResult } from "../interfaces"; import { Router } from "@angular/router"; import { FormGroup, FormBuilder } from "@angular/forms"; import { MatAutocompleteSelectedEvent } from "@angular/material/autocomplete"; import { SearchFilter } from "./SearchFilter"; +import { FilterService } from "../discover/services/filter-service"; @Component({ selector: "app-nav-search", @@ -20,7 +20,6 @@ import { SearchFilter } from "./SearchFilter"; styleUrls: ["./nav-search.component.scss"], }) export class NavSearchComponent implements OnInit { - @Input() public filter: SearchFilter; public selectedItem: string; public results: IMultiSearchResult[]; public searching = false; @@ -28,7 +27,6 @@ export class NavSearchComponent implements OnInit { public searchForm: FormGroup; constructor( - private searchService: SearchV2Service, private router: Router, private fb: FormBuilder ) {} @@ -41,13 +39,14 @@ export class NavSearchComponent implements OnInit { this.searchForm .get("input") .valueChanges.pipe( - debounceTime(600), + debounceTime(1300), tap(() => (this.searching = true)), switchMap((value: string) => { if (value) { - return this.searchService - .multiSearch(value, this.filter) - .pipe(finalize(() => (this.searching = false))); + this.router.navigate([`discover`, value]); + // return this.searchService + // .multiSearch(value, this.filter) + // .pipe(finalize(() => (this.searching = false))); } return empty().pipe(finalize(() => (this.searching = false))); }) diff --git a/src/Ombi/ClientApp/yarn.lock b/src/Ombi/ClientApp/yarn.lock index d2dc99e84..8c55c594b 100644 --- a/src/Ombi/ClientApp/yarn.lock +++ b/src/Ombi/ClientApp/yarn.lock @@ -5242,6 +5242,11 @@ lodash@^4.17.10, lodash@^4.17.11: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" +lodash@^4.17.20: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + log-symbols@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" From b6d67b2e24d1143a9adbe7185616c150dd10cbd0 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Sat, 9 Jan 2021 22:54:25 +0000 Subject: [PATCH 78/82] Added a button for users running the legacy app to launch like they did in V3, this button is in the user preferences area of the site --- .../user-preference.component.html | 3 ++- .../user-preference.component.ts | 18 ++++++++++++++---- src/Ombi/wwwroot/translations/en.json | 3 ++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html index 402c683f1..0b0016c98 100644 --- a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html +++ b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html @@ -25,7 +25,8 @@
- +
diff --git a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.ts b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.ts index 3e4163ea0..7969bdd45 100644 --- a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.ts +++ b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.ts @@ -4,7 +4,7 @@ import { TranslateService } from "@ngx-translate/core"; import { AvailableLanguages, ILanguage } from "./user-preference.constants"; import { StorageService } from "../../../shared/storage/storage-service"; import { IdentityService, NotificationService, SettingsService } from "../../../services"; -import { IUser } from "../../../interfaces"; +import { ICustomizationSettings, IUser } from "../../../interfaces"; @Component({ templateUrl: "./user-preference.component.html", @@ -19,6 +19,7 @@ export class UserPreferenceComponent implements OnInit { public qrCodeEnabled: boolean; public countries: string[]; public selectedCountry: string; + public customizationSettings: ICustomizationSettings; private user: IUser; @@ -33,14 +34,14 @@ export class UserPreferenceComponent implements OnInit { if (user.name) { this.username = user.name; } - const customization = await this.settingsService.getCustomization().toPromise(); + this.customizationSettings = await this.settingsService.getCustomization().toPromise(); this.selectedLang = this.translate.currentLang; const accessToken = await this.identityService.getAccessToken().toPromise(); - this.qrCode = `${customization.applicationUrl}|${accessToken}`; + this.qrCode = `${this.customizationSettings.applicationUrl}|${accessToken}`; - if(!customization.applicationUrl) { + if(!this.customizationSettings.applicationUrl) { this.qrCodeEnabled = false; } else { this.qrCodeEnabled = true; @@ -49,6 +50,7 @@ export class UserPreferenceComponent implements OnInit { this.user = await this.identityService.getUser().toPromise(); this.selectedCountry = this.user.streamingCountry; this.identityService.getSupportedStreamingCountries().subscribe(x => this.countries = x); + this.settingsService.getCustomization().subscribe(x => this.customizationSettings = x); } @@ -61,4 +63,12 @@ export class UserPreferenceComponent implements OnInit { this.identityService.updateStreamingCountry(this.selectedCountry).subscribe(x => this.notification.success(this.translate.instant("UserPreferences.Updated"))); } + public openMobileApp(event: any) { + event.preventDefault(); + + this.identityService.getAccessToken().subscribe(x => { + const url = `ombi://${this.customizationSettings.applicationUrl}_${x}`; + window.location.assign(url); + }); + } } diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index 015a65a00..4ae2ade0b 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -306,6 +306,7 @@ "StreamingCountry":"Streaming Country", "StreamingCountryDescription": "This is the country code that we will display streaming information for. If you are in the US please select US and you will have US related streaming information.", "LanguageDescription": "This is the language you would like the Ombi interface to be displayed in.", - "MobileQRCode":"Mobile QR Code" + "MobileQRCode":"Mobile QR Code", + "LegacyApp":"Launch Legacy App" } } \ No newline at end of file From 938e8439bc742a949eafe7048e89847839c7098d Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Sun, 10 Jan 2021 11:11:22 +0000 Subject: [PATCH 79/82] fixed the search --- .../search-results/search-results.component.html | 7 ++++++- .../components/search-results/search-results.component.ts | 4 +++- src/Ombi/ClientApp/src/app/my-nav/SearchFilter.ts | 3 +++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html index e6c372b57..0dc51b46c 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html +++ b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html @@ -2,9 +2,14 @@
-
+
+
+
+

Sorry, nothing matches your search!

+
+
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts index b43581af8..32ea9199a 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts @@ -11,7 +11,7 @@ import { isEqual } from "lodash"; @Component({ templateUrl: "./search-results.component.html", - styleUrls: ["./search-results.component.scss"], + styleUrls: ["../discover/discover.component.scss"], }) export class DiscoverSearchResultsComponent implements OnInit { @@ -49,6 +49,8 @@ export class DiscoverSearchResultsComponent implements OnInit { var filter = this.store.get("searchFilter"); if (filter) { this.filter = Object.assign(new SearchFilter(), JSON.parse(filter)); + } else { + this.filter = new SearchFilter({ movies: true, tvShows: true, people: false, music: false}); } this.loading(); await this.search(); diff --git a/src/Ombi/ClientApp/src/app/my-nav/SearchFilter.ts b/src/Ombi/ClientApp/src/app/my-nav/SearchFilter.ts index 40af3af79..c0a1f3a9b 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/SearchFilter.ts +++ b/src/Ombi/ClientApp/src/app/my-nav/SearchFilter.ts @@ -4,4 +4,7 @@ export class SearchFilter { tvShows: boolean; music: boolean; people: boolean; + public constructor(init?:Partial) { + Object.assign(this, init); +} } From 2ffec6b1e65de46df4bb18f2ab7d3bb72c51b702 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Sun, 10 Jan 2021 11:47:08 +0000 Subject: [PATCH 80/82] minor code cleanuo --- .../card/discover-card.component.html | 4 +- .../search-results.component.html | 2 +- .../search-results.component.ts | 35 ----------------- .../src/app/my-nav/nav-search.component.html | 39 +------------------ .../src/app/my-nav/nav-search.component.ts | 38 ++---------------- src/Ombi/wwwroot/translations/en.json | 3 +- 6 files changed, 9 insertions(+), 112 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html index 57581bb32..9bbe868ee 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html @@ -1,9 +1,9 @@
- +
{{getAvailbilityStatus()}}
- + {{result.title}} diff --git a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html index 0dc51b46c..965017163 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html +++ b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html @@ -9,7 +9,7 @@
-

Sorry, nothing matches your search!

+

{{'Discovery.NoSearch' | translate}}

\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts index 32ea9199a..995389b2e 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts @@ -85,44 +85,9 @@ export class DiscoverSearchResultsComponent implements OnInit { available: false, tvMovieDb: mediaType === RequestType.tvShow ? true : false }); - - // switch (mediaType) { - // case RequestType.movie: - // this.searchService.getFullMovieDetails(+m.id) - // .subscribe(x => { - // const index = this.discoverResults.findIndex((obj => obj.id === +m.id)); - // this.discoverResults[index].available = x.available; - // this.discoverResults[index].requested = x.requested; - // this.discoverResults[index].requested = x.requested; - // this.discoverResults[index].requested = x.requested; - // this.discoverResults[index].requested = x.requested; - // this.discoverResults[index].requested = x.requested; - // }); - // } }); } - // private createModel() { - // this.finishLoading(); - // this.collection.collection.forEach(m => { - // this.discoverResults.push({ - // available: m.available, - // posterPath: `https://image.tmdb.org/t/p/w300/${m.posterPath}`, - // requested: m.requested, - // title: m.title, - // type: RequestType.movie, - // id: m.id, - // url: `http://www.imdb.com/title/${m.imdbId}/`, - // rating: 0, - // overview: m.overview, - // approved: m.approved, - // imdbid: m.imdbId, - // denied:false, - // background: "" - // }); - // }); - // } - private loading() { this.loadingFlag = true; } diff --git a/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.html b/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.html index ccf574725..da8fa5d96 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.html +++ b/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.html @@ -1,42 +1,5 @@ - -
- + - - - - - - - - - -   - {{result.title}} - - -   - - {{result.title}} - - - -   - - {{result.title}} - - - -   - {{result.title}} - - - - \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.ts b/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.ts index 4474aee4e..4d4573cd7 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.ts +++ b/src/Ombi/ClientApp/src/app/my-nav/nav-search.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from "@angular/core"; +import { Component, OnInit } from "@angular/core"; import { debounceTime, switchMap, @@ -7,12 +7,8 @@ import { } from "rxjs/operators"; import { empty} from "rxjs"; -import { IMultiSearchResult } from "../interfaces"; import { Router } from "@angular/router"; import { FormGroup, FormBuilder } from "@angular/forms"; -import { MatAutocompleteSelectedEvent } from "@angular/material/autocomplete"; -import { SearchFilter } from "./SearchFilter"; -import { FilterService } from "../discover/services/filter-service"; @Component({ selector: "app-nav-search", @@ -20,9 +16,6 @@ import { FilterService } from "../discover/services/filter-service"; styleUrls: ["./nav-search.component.scss"], }) export class NavSearchComponent implements OnInit { - public selectedItem: string; - public results: IMultiSearchResult[]; - public searching = false; public searchForm: FormGroup; @@ -40,38 +33,13 @@ export class NavSearchComponent implements OnInit { .get("input") .valueChanges.pipe( debounceTime(1300), - tap(() => (this.searching = true)), switchMap((value: string) => { if (value) { this.router.navigate([`discover`, value]); - // return this.searchService - // .multiSearch(value, this.filter) - // .pipe(finalize(() => (this.searching = false))); } - return empty().pipe(finalize(() => (this.searching = false))); + return empty();; }) ) - .subscribe((r) => (this.results = r)); - } - - public selected(event: MatAutocompleteSelectedEvent) { - this.searchForm.controls.input.setValue(null); - const val = event.option.value as IMultiSearchResult; - if (val.mediaType == "movie") { - this.router.navigate([`details/movie/${val.id}`]); - return; - } else if (val.mediaType == "tv") { - this.router.navigate([`details/tv/${val.id}/true`]); - return; - } else if (val.mediaType == "person") { - this.router.navigate([`discover/actor/${val.id}`]); - return; - } else if (val.mediaType == "Artist") { - this.router.navigate([`details/artist/${val.id}`]); - return; - } - } - displayFn(result: IMultiSearchResult) { - if (result) { return result.title; } + .subscribe(); } } diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index 4ae2ade0b..60ac07f73 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -296,7 +296,8 @@ "FirstAired": "First Aired", "Writer": "Writer", "ExecProducer": "Exec Producer" - } + }, + "NoSearch":"Sorry, nothing matches your search!" }, "UserPreferences": { "Welcome": "Welcome {{username}}!", From df74e386726c472057a1e5ebd0a848ff4f0b3c0a Mon Sep 17 00:00:00 2001 From: tidusjar Date: Mon, 11 Jan 2021 14:13:26 +0000 Subject: [PATCH 81/82] Fixed the artist search in ombi Make minor improvements to the new search result --- src/Ombi.Api.MusicBrainz/IMusicBrainzApi.cs | 2 + src/Ombi.Api.MusicBrainz/MusicBrainzApi.cs | 7 +++ .../Engine/Interfaces/IMusicSearchEngineV2.cs | 1 + .../Engine/V2/MusicSearchEngineV2.cs | 37 +++++++++--- .../card/discover-card-details.component.ts | 6 +- .../card/discover-card.component.html | 2 +- .../card/discover-card.component.ts | 41 ++++++++++++-- .../grid/discover-grid.component.ts | 56 +++++++++---------- .../search-results.component.ts | 18 ++++-- .../ClientApp/src/app/discover/interfaces.ts | 2 +- .../src/app/services/search.service.ts | 3 + .../src/app/services/searchV2.service.ts | 6 +- src/Ombi/Controllers/V2/SearchController.cs | 9 +++ src/Ombi/Program.cs | 2 + 14 files changed, 141 insertions(+), 51 deletions(-) diff --git a/src/Ombi.Api.MusicBrainz/IMusicBrainzApi.cs b/src/Ombi.Api.MusicBrainz/IMusicBrainzApi.cs index 69d8231ad..3b47cdd28 100644 --- a/src/Ombi.Api.MusicBrainz/IMusicBrainzApi.cs +++ b/src/Ombi.Api.MusicBrainz/IMusicBrainzApi.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Hqub.MusicBrainz.API.Entities; +using Hqub.MusicBrainz.API.Entities.Collections; using Ombi.Api.MusicBrainz.Models; namespace Ombi.Api.MusicBrainz @@ -11,6 +12,7 @@ namespace Ombi.Api.MusicBrainz Task> SearchArtist(string artistQuery); Task> GetReleaseForArtist(string artistId); Task GetArtistInformation(string artistId); + Task GetAlbumInformation(string albumId); Task GetCoverArtForReleaseGroup(string musicBrainzId, CancellationToken token); } } \ No newline at end of file diff --git a/src/Ombi.Api.MusicBrainz/MusicBrainzApi.cs b/src/Ombi.Api.MusicBrainz/MusicBrainzApi.cs index 3c86d7882..e870eb07f 100644 --- a/src/Ombi.Api.MusicBrainz/MusicBrainzApi.cs +++ b/src/Ombi.Api.MusicBrainz/MusicBrainzApi.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Hqub.MusicBrainz.API; using Hqub.MusicBrainz.API.Entities; +using Hqub.MusicBrainz.API.Entities.Collections; using Newtonsoft.Json; using Ombi.Api.MusicBrainz.Models; @@ -20,6 +21,12 @@ namespace Ombi.Api.MusicBrainz _api = api; } + public Task GetAlbumInformation(string albumId) + { + var album = Release.GetAsync(albumId); + return album; + } + public async Task> SearchArtist(string artistQuery) { var artist = await Artist.SearchAsync(artistQuery, 10); diff --git a/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs b/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs index f9889ec9c..efd42dc68 100644 --- a/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs @@ -9,5 +9,6 @@ namespace Ombi.Core.Engine.Interfaces Task GetArtistInformation(string artistId); Task GetArtistInformationByRequestId(int requestId); Task GetReleaseGroupArt(string musicBrainzId, CancellationToken token); + Task GetAlbum(string albumId); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/V2/MusicSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/MusicSearchEngineV2.cs index 13256be0a..b7d9575db 100644 --- a/src/Ombi.Core/Engine/V2/MusicSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/MusicSearchEngineV2.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Security.Principal; using System.Threading; using System.Threading.Tasks; +using Newtonsoft.Json; using Ombi.Api.Lidarr; using Ombi.Api.Lidarr.Models; using Ombi.Api.MusicBrainz; @@ -41,6 +42,21 @@ namespace Ombi.Core.Engine.V2 _lidarrApi = lidarrApi; } + public async Task GetAlbum(string albumId) + { + var g = await _musicBrainzApi.GetAlbumInformation(albumId); + var release = new ReleaseGroup + { + ReleaseType = g.ReleaseGroup.PrimaryType, + Id = g.Id, + Title = g.Title, + ReleaseDate = g.ReleaseGroup.FirstReleaseDate, + }; + + await RunSearchRules(release); + return release; + } + public async Task GetArtistInformation(string artistId) { var artist = await _musicBrainzApi.GetArtistInformation(artistId); @@ -84,12 +100,19 @@ namespace Ombi.Core.Engine.V2 if (lidarrArtistTask != null) { - var artistResult = await lidarrArtistTask; - info.Banner = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("banner", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl(); - info.Logo = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("logo", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl(); - info.Poster = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("poster", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl(); - info.FanArt = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("fanart", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl(); - info.Overview = artistResult.overview; + try + { + var artistResult = await lidarrArtistTask; + info.Banner = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("banner", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl(); + info.Logo = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("logo", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl(); + info.Poster = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("poster", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl(); + info.FanArt = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("fanart", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl(); + info.Overview = artistResult.overview; + } + catch (JsonSerializationException) + { + // swallow, Lidarr probably doesn't have this artist + } } return info; @@ -118,7 +141,7 @@ namespace Ombi.Core.Engine.V2 return new AlbumArt(); } - + public async Task GetArtistInformationByRequestId(int requestId) { var request = await RequestService.MusicRequestRepository.Find(requestId); diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.ts b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.ts index f3d1276ad..883add54a 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.ts @@ -31,9 +31,9 @@ export class DiscoverCardDetailsComponent implements OnInit { public async ngOnInit() { this.loading = true; if (this.data.type === RequestType.movie) { - this.movie = await this.searchService.getFullMovieDetailsPromise(this.data.id); + this.movie = await this.searchService.getFullMovieDetailsPromise(+this.data.id); } else if (this.data.type === RequestType.tvShow) { - this.tv = await this.searchService.getTvInfo(this.data.id); + this.tv = await this.searchService.getTvInfo(+this.data.id); const creator = this.tv.crew.filter(tv => { return tv.type === "Creator"; })[0]; @@ -67,7 +67,7 @@ export class DiscoverCardDetailsComponent implements OnInit { public async request() { this.loading = true; if (this.data.type === RequestType.movie) { - const result = await this.requestService.requestMovie({ theMovieDbId: this.data.id, languageCode: "", requestOnBehalf: null }).toPromise(); + const result = await this.requestService.requestMovie({ theMovieDbId: +this.data.id, languageCode: "", requestOnBehalf: null }).toPromise(); this.loading = false; if (result.result) { diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html index 9bbe868ee..b32bb98d8 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.html @@ -1,5 +1,5 @@
- +
{{getAvailbilityStatus()}}
diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts index c923de72b..6512ca3a8 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit, Input } from "@angular/core"; import { IDiscoverCardResult } from "../../interfaces"; import { RequestType } from "../../../interfaces"; -import { SearchV2Service } from "../../../services"; +import { SearchService, SearchV2Service } from "../../../services"; import { MatDialog } from "@angular/material/dialog"; import { DiscoverCardDetailsComponent } from "./discover-card-details.component"; import { ISearchTvResultV2 } from "../../../interfaces/ISearchTvResultV2"; @@ -18,7 +18,7 @@ export class DiscoverCardComponent implements OnInit { public RequestType = RequestType; public hide: boolean; - constructor(private searchService: SearchV2Service, private dialog: MatDialog) { } + constructor(private searchService: SearchV2Service, private v1Search: SearchService, private dialog: MatDialog) { } public ngOnInit() { if (this.result.type == RequestType.tvShow) { @@ -27,6 +27,9 @@ export class DiscoverCardComponent implements OnInit { if (this.result.type == RequestType.movie) { this.getExtraMovieInfo(); } + if (this.result.type == RequestType.album) { + this.getAlbumInformation(); + } } public openDetails(details: IDiscoverCardResult) { @@ -35,9 +38,9 @@ export class DiscoverCardComponent implements OnInit { public async getExtraTvInfo() { if (this.result.tvMovieDb) { - var result = await this.searchService.getTvInfoWithMovieDbId(this.result.id); + var result = await this.searchService.getTvInfoWithMovieDbId(+this.result.id); } else { - var result = await this.searchService.getTvInfo(this.result.id); + var result = await this.searchService.getTvInfo(+this.result.id); } if(result.status === "404") { this.hide = true; @@ -48,6 +51,34 @@ export class DiscoverCardComponent implements OnInit { } + public async getAlbumInformation() { + this.searchService.getArtistInformation(this.result.id.toString()).subscribe(x => { + if (x.poster) { + this.result.posterPath = x.poster; + } else { + this.searchService.getReleaseGroupArt(this.result.id.toString()).subscribe(art => { + if(art.image) { + this.result.posterPath = art.image; + } + }) + } + this.result.title = x.startYear ? `${x.name} (${x.startYear})` : x.name; + this.result.overview = x.overview; + }); + } + + public generateDetailsLink(): string { + let link = ""; + switch(this.result.type){ + case RequestType.movie: + return `/details/movie/${this.result.id}`; + case RequestType.tvShow: + return `/details/tv/${this.result.id}`; + case RequestType.album: //Actually artist + return `/details/artist/${this.result.id}`; + } + } + public getStatusClass(): string { if (this.result.available) { return "available"; @@ -76,7 +107,7 @@ export class DiscoverCardComponent implements OnInit { private getExtraMovieInfo() { if (!this.result.imdbid) { - this.searchService.getFullMovieDetails(this.result.id) + this.searchService.getFullMovieDetails(+this.result.id) .subscribe(m => { this.updateMovieItem(m); }); diff --git a/src/Ombi/ClientApp/src/app/discover/components/grid/discover-grid.component.ts b/src/Ombi/ClientApp/src/app/discover/components/grid/discover-grid.component.ts index 42f371dc2..51ea7a85f 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/grid/discover-grid.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/grid/discover-grid.component.ts @@ -27,8 +27,8 @@ export class DiscoverGridComponent implements OnInit { public movie: ISearchMovieResultV2; constructor(private searchService: SearchV2Service, private dialog: MatDialog, - private requestService: RequestService, private notification: MatSnackBar, - private router: Router, private sanitizer: DomSanitizer, private imageService: ImageService) { } + private requestService: RequestService, private notification: MatSnackBar, + private router: Router, private sanitizer: DomSanitizer, private imageService: ImageService) { } public ngOnInit() { if (this.result.type == RequestType.tvShow) { @@ -40,7 +40,7 @@ export class DiscoverGridComponent implements OnInit { } public async getExtraTvInfo() { - this.tv = await this.searchService.getTvInfo(this.result.id); + this.tv = await this.searchService.getTvInfo(+this.result.id); this.setTvDefaults(this.tv); this.updateTvItem(this.tv); const creator = this.tv.crew.filter(tv => { @@ -80,35 +80,33 @@ export class DiscoverGridComponent implements OnInit { } private getExtraMovieInfo() { - // if (!this.result.imdbid) { - this.searchService.getFullMovieDetails(this.result.id) - .subscribe(m => { - this.movie = m; - this.updateMovieItem(m); - }); + this.searchService.getFullMovieDetails(+this.result.id) + .subscribe(m => { + this.movie = m; + this.updateMovieItem(m); + }); - this.setMovieBackground() - // } + this.setMovieBackground() } private setMovieBackground(): void { - this.result.background = this.sanitizer.bypassSecurityTrustStyle - ("linear-gradient( rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5) ), url(" + "https://image.tmdb.org/t/p/original" + this.result.background + ")"); - } + this.result.background = this.sanitizer.bypassSecurityTrustStyle + ("linear-gradient( rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5) ), url(" + "https://image.tmdb.org/t/p/original" + this.result.background + ")"); + } - private setTvBackground(): void { - if (this.result.background != null) { - this.result.background = this.sanitizer.bypassSecurityTrustStyle - ("linear-gradient( rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5) ), url(https://image.tmdb.org/t/p/original" + this.result.background + ")"); - } else { - this.imageService.getTvBanner(this.result.id).subscribe(x => { - if (x) { - this.result.background = this.sanitizer.bypassSecurityTrustStyle - ("linear-gradient( rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5) ), url(" + x + ")"); - } - }); - } - } + private setTvBackground(): void { + if (this.result.background != null) { + this.result.background = this.sanitizer.bypassSecurityTrustStyle + ("linear-gradient( rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5) ), url(https://image.tmdb.org/t/p/original" + this.result.background + ")"); + } else { + this.imageService.getTvBanner(+this.result.id).subscribe(x => { + if (x) { + this.result.background = this.sanitizer.bypassSecurityTrustStyle + ("linear-gradient( rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5) ), url(" + x + ")"); + } + }); + } + } private updateMovieItem(updated: ISearchMovieResultV2) { this.result.url = "http://www.imdb.com/title/" + updated.imdbId + "/"; @@ -139,7 +137,7 @@ export class DiscoverGridComponent implements OnInit { public async request() { this.requesting = true; if (this.result.type === RequestType.movie) { - const result = await this.requestService.requestMovie({ theMovieDbId: this.result.id, languageCode: "", requestOnBehalf: null }).toPromise(); + const result = await this.requestService.requestMovie({ theMovieDbId: +this.result.id, languageCode: "", requestOnBehalf: null }).toPromise(); if (result.result) { this.result.requested = true; @@ -148,7 +146,7 @@ export class DiscoverGridComponent implements OnInit { this.notification.open(result.errorMessage, "Ok"); } } else if (this.result.type === RequestType.tvShow) { - this.dialog.open(EpisodeRequestComponent, { width: "700px", data: { series: this.tv, requestOnBehalf: null }, panelClass: 'modal-panel' }) + this.dialog.open(EpisodeRequestComponent, { width: "700px", data: { series: this.tv, requestOnBehalf: null }, panelClass: 'modal-panel' }) } this.requesting = false; } diff --git a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts index 995389b2e..0b2aa2030 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts @@ -39,7 +39,7 @@ export class DiscoverSearchResultsComponent implements OnInit { this.filterService.onFilterChange.subscribe(async x => { if (!isEqual(this.filter, x)) { - this.filter = { ...x }; + this.filter = { ...x }; await this.search(); } }); @@ -50,7 +50,7 @@ export class DiscoverSearchResultsComponent implements OnInit { if (filter) { this.filter = Object.assign(new SearchFilter(), JSON.parse(filter)); } else { - this.filter = new SearchFilter({ movies: true, tvShows: true, people: false, music: false}); + this.filter = new SearchFilter({ movies: true, tvShows: true, people: false, music: false }); } this.loading(); await this.search(); @@ -69,12 +69,22 @@ export class DiscoverSearchResultsComponent implements OnInit { mediaType = RequestType.album; } + let poster = `https://image.tmdb.org/t/p/w300/${m.poster}`; + if (!m.poster) { + if (mediaType === RequestType.movie) { + poster = "images/default_movie_poster.png" + } + if (mediaType === RequestType.tvShow) { + poster = "images/default_tv_poster.png" + } + } + this.discoverResults.push({ - posterPath: `https://image.tmdb.org/t/p/w300/${m.poster}`, + posterPath: mediaType !== RequestType.album ? poster : "images/default-music-placeholder.png", requested: false, title: m.title, type: mediaType, - id: +m.id, + id: m.id, url: "", rating: 0, overview: "", diff --git a/src/Ombi/ClientApp/src/app/discover/interfaces.ts b/src/Ombi/ClientApp/src/app/discover/interfaces.ts index bac0faafc..7127f52c5 100644 --- a/src/Ombi/ClientApp/src/app/discover/interfaces.ts +++ b/src/Ombi/ClientApp/src/app/discover/interfaces.ts @@ -1,7 +1,7 @@ import { RequestType } from "../interfaces"; export interface IDiscoverCardResult { - id: number; + id: number | string; posterPath: string; url: string | undefined; title: string; diff --git a/src/Ombi/ClientApp/src/app/services/search.service.ts b/src/Ombi/ClientApp/src/app/services/search.service.ts index 0adfed3ad..3468bede4 100644 --- a/src/Ombi/ClientApp/src/app/services/search.service.ts +++ b/src/Ombi/ClientApp/src/app/services/search.service.ts @@ -89,6 +89,9 @@ export class SearchService extends ServiceHelpers { public searchAlbum(searchTerm: string): Observable { return this.http.get(`${this.url}/Music/Album/` + searchTerm); } + public getAlbumInformation(foreignAlbumId: string): Observable { + return this.http.get(`${this.url}/Music/Album/info/` + foreignAlbumId); + } public getAlbumsForArtist(foreignArtistId: string): Observable { return this.http.get(`${this.url}/Music/Artist/Album/${foreignArtistId}`); } diff --git a/src/Ombi/ClientApp/src/app/services/searchV2.service.ts b/src/Ombi/ClientApp/src/app/services/searchV2.service.ts index fa970313f..383ee18d9 100644 --- a/src/Ombi/ClientApp/src/app/services/searchV2.service.ts +++ b/src/Ombi/ClientApp/src/app/services/searchV2.service.ts @@ -9,7 +9,7 @@ import { ServiceHelpers } from "./service.helpers"; import { ISearchMovieResultV2 } from "../interfaces/ISearchMovieResultV2"; import { ISearchTvResultV2, IMovieCollectionsViewModel, IActorCredits } from "../interfaces/ISearchTvResultV2"; -import { IArtistSearchResult, IAlbumArt } from "../interfaces/IMusicSearchResultV2"; +import { IArtistSearchResult, IAlbumArt, IReleaseGroups } from "../interfaces/IMusicSearchResultV2"; import { SearchFilter } from "../my-nav/SearchFilter"; import { IMovieRatings, ITvRatings } from "../interfaces/IRatings"; import { IStreamingData } from "../interfaces/IStreams"; @@ -124,6 +124,10 @@ export class SearchV2Service extends ServiceHelpers { return this.http.get(`${this.url}/releasegroupart/${mbid}`); } + public getAlbum(mbid: string): Observable { + return this.http.get(`${this.url}/artist/album/${mbid}`); + } + public getRottenMovieRatings(name: string, year: number): Observable { return this.http.get(`${this.url}/ratings/movie/${name}/${year}`); } diff --git a/src/Ombi/Controllers/V2/SearchController.cs b/src/Ombi/Controllers/V2/SearchController.cs index 889dc288f..ed831bb1c 100644 --- a/src/Ombi/Controllers/V2/SearchController.cs +++ b/src/Ombi/Controllers/V2/SearchController.cs @@ -16,6 +16,7 @@ using Ombi.Core.Models.Search.V2.Music; using Ombi.Models; using Ombi.Api.RottenTomatoes.Models; using Ombi.Api.RottenTomatoes; +using Hqub.MusicBrainz.API.Entities.Collections; namespace Ombi.Controllers.V2 { @@ -396,6 +397,14 @@ namespace Ombi.Controllers.V2 return await _musicEngine.GetArtistInformationByRequestId(requestId); } + [HttpGet("artist/album/{albumId}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesDefaultResponseType] + public Task GetAlbumInformation(string albumId) + { + return _musicEngine.GetAlbum(albumId); + } + [HttpGet("releasegroupart/{musicBrainzId}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesDefaultResponseType] diff --git a/src/Ombi/Program.cs b/src/Ombi/Program.cs index 46b5c8559..f8a45387e 100644 --- a/src/Ombi/Program.cs +++ b/src/Ombi/Program.cs @@ -45,6 +45,8 @@ namespace Ombi } }); + + Console.WriteLine(HelpOutput(result)); UrlArgs = host; From 75c09945075a55974add0b1f29502cabe9f5b194 Mon Sep 17 00:00:00 2001 From: Jamie Date: Mon, 11 Jan 2021 16:42:10 +0000 Subject: [PATCH 82/82] Updated deployment for the new org --- .azuredevops/pipelines/publish-job.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.azuredevops/pipelines/publish-job.yml b/.azuredevops/pipelines/publish-job.yml index 24c24d385..92b3d7ed4 100644 --- a/.azuredevops/pipelines/publish-job.yml +++ b/.azuredevops/pipelines/publish-job.yml @@ -77,8 +77,8 @@ stages: - task: GitHubRelease@1 inputs: - gitHubConnection: 'github.com_tidusjar' - repositoryName: 'tidusjar/Ombi.Releases' + gitHubConnection: 'PAT' + repositoryName: 'Ombi-io/Ombi.Releases' action: 'create' target: 'c7fcbb77b58aef1076d635a9ef99e4374abc8672' tagSource: 'userSpecifiedTag'
Local User Plex User - Emby User