From 924a562c57f4126345b1a51dfd625136af67a2f6 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 28 Jul 2021 21:40:49 +0100 Subject: [PATCH] Added the basic advanced search --- .../Engine/V2/MovieSearchEngineV2.cs | 12 +-- src/Ombi.TheMovieDbApi/TheMovieDbApi.cs | 2 +- .../src/app/discover/components/index.ts | 17 +++-- .../search-results.component.ts | 76 +++++++++++++++++-- .../src/app/discover/discover.module.ts | 14 ++-- .../src/app/my-nav/my-nav.component.scss | 10 +++ .../src/app/my-nav/my-nav.component.ts | 14 +++- .../advanced-search-dialog-data.service.ts | 27 +++++++ .../advanced-search-dialog.component.html | 30 +++++--- .../advanced-search-dialog.component.ts | 32 +++++--- .../genre-select/genre-select.component.html | 2 +- .../keyword-search.component.html | 15 +--- .../watch-providers-select.component.html | 4 +- src/Ombi/wwwroot/translations/en.json | 2 + 14 files changed, 185 insertions(+), 72 deletions(-) create mode 100644 src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog-data.service.ts diff --git a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs index acc390aec..e865c6465 100644 --- a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs @@ -147,15 +147,15 @@ public async Task> AdvancedSearch(DiscoverMode { var langCode = await DefaultLanguageCode(null); - var pages = PaginationHelper.GetNextPages(currentlyLoaded, toLoad, _theMovieDbMaxPageItems); + //var pages = PaginationHelper.GetNextPages(currentlyLoaded, toLoad, _theMovieDbMaxPageItems); var results = new List(); - foreach (var pagesToLoad in pages) - { + //foreach (var pagesToLoad in pages) + //{ var apiResult = await MovieApi.AdvancedSearch(model, cancellationToken); - results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take)); - } - return await TransformMovieResultsToResponse(results); + //results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take)); + //} + return await TransformMovieResultsToResponse(apiResult); } /// diff --git a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs index 7e89b48fd..aa46b0c8e 100644 --- a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs @@ -90,7 +90,7 @@ public async Task> AdvancedSearch(DiscoverModel model, { request.FullUri = request.FullUri.AddQueryParameter("with_watch_providers", string.Join(',', model.WatchProviders)); } - request.FullUri = request.FullUri.AddQueryParameter("sort_by", "popularity.desc"); + //request.FullUri = request.FullUri.AddQueryParameter("sort_by", "popularity.desc"); var result = await Api.Request>(request, cancellationToken); return Mapper.Map>(result.results); diff --git a/src/Ombi/ClientApp/src/app/discover/components/index.ts b/src/Ombi/ClientApp/src/app/discover/components/index.ts index 2d399cd76..835ef702c 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/index.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/index.ts @@ -1,15 +1,15 @@ -import { DiscoverComponent } from "./discover/discover.component"; -import { DiscoverCollectionsComponent } from "./collections/discover-collections.component"; +import { RadarrService, RequestService, SearchService, SonarrService } from "../../services"; + +import { AuthGuard } from "../../auth/auth.guard"; +import { CarouselListComponent } from "./carousel-list/carousel-list.component"; import { DiscoverActorComponent } from "./actor/discover-actor.component"; import { DiscoverCardComponent } from "./card/discover-card.component"; -import { Routes } from "@angular/router"; -import { AuthGuard } from "../../auth/auth.guard"; -import { SearchService, RequestService, SonarrService, RadarrService } from "../../services"; -import { MatDialog } from "@angular/material/dialog"; +import { DiscoverCollectionsComponent } from "./collections/discover-collections.component"; +import { DiscoverComponent } from "./discover/discover.component"; import { DiscoverSearchResultsComponent } from "./search-results/search-results.component"; -import { CarouselListComponent } from "./carousel-list/carousel-list.component"; +import { MatDialog } from "@angular/material/dialog"; import { RequestServiceV2 } from "../../services/requestV2.service"; - +import { Routes } from "@angular/router"; export const components: any[] = [ DiscoverComponent, @@ -34,4 +34,5 @@ export const routes: Routes = [ { path: "collection/:collectionId", component: DiscoverCollectionsComponent, canActivate: [AuthGuard] }, { path: "actor/:actorId", component: DiscoverActorComponent, canActivate: [AuthGuard] }, { path: ":searchTerm", component: DiscoverSearchResultsComponent, canActivate: [AuthGuard] }, + { path: "advanced/search", 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.ts b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts index d9e046da9..02415a970 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 @@ -1,14 +1,15 @@ +import { ActivatedRoute, NavigationEnd, Router } from "@angular/router"; 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 { IMultiSearchResult, ISearchMovieResult, RequestType } from "../../../interfaces"; + +import { AdvancedSearchDialogDataService } from "../../../shared/advanced-search-dialog/advanced-search-dialog-data.service"; +import { AuthService } from "../../../auth/auth.service"; import { FilterService } from "../../services/filter-service"; +import { IDiscoverCardResult } from "../../interfaces"; import { SearchFilter } from "../../../my-nav/SearchFilter"; +import { SearchV2Service } from "../../../services"; import { StorageService } from "../../../shared/storage/storage-service"; - import { isEqual } from "lodash"; -import { AuthService } from "../../../auth/auth.service"; @Component({ templateUrl: "./search-results.component.html", @@ -25,22 +26,41 @@ export class DiscoverSearchResultsComponent implements OnInit { public filter: SearchFilter; + private isAdvancedSearch: boolean; + constructor(private searchService: SearchV2Service, private route: ActivatedRoute, private filterService: FilterService, + private router: Router, + private advancedDataService: AdvancedSearchDialogDataService, private store: StorageService, private authService: AuthService) { this.route.params.subscribe((params: any) => { + this.isAdvancedSearch = this.router.url === '/discover/advanced/search'; + if (this.isAdvancedSearch) { + this.loadAdvancedData(); + return; + } this.searchTerm = params.searchTerm; this.clear(); this.init(); }); + + this.advancedDataService.onDataChange.subscribe(() => { + this.clear(); + this.loadAdvancedData(); + }); + } public async ngOnInit() { - this.loadingFlag = true; this.isAdmin = this.authService.isAdmin(); + if (this.advancedDataService) { + return; + } + this.loadingFlag = true; + this.filterService.onFilterChange.subscribe(async x => { if (!isEqual(this.filter, x)) { this.filter = { ...x }; @@ -115,6 +135,48 @@ export class DiscoverSearchResultsComponent implements OnInit { this.discoverResults = []; } + private loadAdvancedData() { + const advancedData = this.advancedDataService.getData(); + this.mapAdvancedData(advancedData); + return; + } + + public mapAdvancedData(advancedData: ISearchMovieResult[]) { + this.finishLoading(); + const type = this.advancedDataService.getType(); + advancedData.forEach(m => { + + let mediaType = type; + + let poster = `https://image.tmdb.org/t/p/w300/${m.posterPath}`; + if (!m.posterPath) { + if (mediaType === RequestType.movie) { + poster = "images/default_movie_poster.png" + } + if (mediaType === RequestType.tvShow) { + poster = "images/default_tv_poster.png" + } + } + + this.discoverResults.push({ + posterPath: poster, + requested: false, + title: m.title, + type: mediaType, + id: m.id, + url: "", + rating: 0, + overview: m.overview, + approved: false, + imdbid: "", + denied: false, + background: "", + available: false, + tvMovieDb: false + }); + }); + } + private async search() { this.clear(); this.results = await this.searchService diff --git a/src/Ombi/ClientApp/src/app/discover/discover.module.ts b/src/Ombi/ClientApp/src/app/discover/discover.module.ts index 2514f0636..414c881e6 100644 --- a/src/Ombi/ClientApp/src/app/discover/discover.module.ts +++ b/src/Ombi/ClientApp/src/app/discover/discover.module.ts @@ -1,16 +1,14 @@ -import { NgModule } from "@angular/core"; -import { RouterModule } from "@angular/router"; +import * as fromComponents from './components'; + +import { CarouselModule } from 'primeng/carousel'; import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import {MatButtonToggleModule} from '@angular/material/button-toggle'; - -import { SharedModule } from "../shared/shared.module"; +import { NgModule } from "@angular/core"; import { PipeModule } from "../pipes/pipe.module"; -import { CarouselModule } from 'primeng/carousel'; +import { RouterModule } from "@angular/router"; +import { SharedModule } from "../shared/shared.module"; import { SkeletonModule } from 'primeng/skeleton'; -import * as fromComponents from './components'; - - @NgModule({ imports: [ RouterModule.forChild(fromComponents.routes), 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 4f17a636d..40406fe05 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 @@ -234,4 +234,14 @@ .advanced-search { margin-left: 10px; +} + +::ng-deep .dialog-responsive { + width: 40%; +} + +@media only screen and (max-width: 760px) { + ::ng-deep .dialog-responsive { + width: 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 393d12fd4..10c2b5998 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 @@ -11,6 +11,7 @@ import { MatDialog } from '@angular/material/dialog'; import { MatSlideToggleChange } from '@angular/material/slide-toggle'; import { Md5 } from 'ts-md5/dist/md5'; import { Observable } from 'rxjs'; +import { Router } from '@angular/router'; import { SearchFilter } from './SearchFilter'; import { StorageService } from '../shared/storage/storage-service'; import { map } from 'rxjs/operators'; @@ -57,7 +58,8 @@ export class MyNavComponent implements OnInit { private store: StorageService, private filterService: FilterService, private dialogService: MatDialog, - private readonly settingState: SettingsStateService) { + private readonly settingState: SettingsStateService, + private router: Router) { } public async ngOnInit() { @@ -125,7 +127,15 @@ export class MyNavComponent implements OnInit { } public openAdvancedSearch() { - this.dialogService.open(AdvancedSearchDialogComponent, null); + const dialogRef = this.dialogService.open(AdvancedSearchDialogComponent, { panelClass: 'dialog-responsive' }); + + dialogRef.afterClosed().subscribe(result => { + if (result) { + this.router.navigate([`discover/advanced/search`]); + } + + return; + }); } public getUserImage(): string { diff --git a/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog-data.service.ts b/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog-data.service.ts new file mode 100644 index 000000000..c6312915a --- /dev/null +++ b/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog-data.service.ts @@ -0,0 +1,27 @@ +import { EventEmitter, Injectable, Output } from "@angular/core"; + +import { RequestType } from "../../interfaces"; + +@Injectable({ + providedIn: "root" +}) +export class AdvancedSearchDialogDataService { + + @Output() public onDataChange = new EventEmitter(); + private _data: any; + private _type: RequestType; + + setData(data: any, type: RequestType) { + this._data = data; + this._type = type; + this.onDataChange.emit(this._data); + } + + getData(): any { + return this._data; + } + + getType(): RequestType { + return this._type; + } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog.component.html b/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog.component.html index 67bd1c2d5..4c8e58821 100644 --- a/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog.component.html +++ b/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog.component.html @@ -4,8 +4,8 @@


@@ -13,7 +13,10 @@

-
+
+ Please choose what type of media you are searching for: +
+
Movies @@ -21,23 +24,26 @@

-
- - Year +
+ + Year of Release
-
- -
-
+ +
- -
+ +
+
+ + Please note that Keyword Searching is very hit and miss due to the inconsistent data in TheMovieDb + +
diff --git a/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog.component.ts b/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog.component.ts index a06d43b43..614d2d103 100644 --- a/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog.component.ts +++ b/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog.component.ts @@ -1,9 +1,9 @@ import { Component, Inject, OnInit } from "@angular/core"; import { FormBuilder, FormGroup } from "@angular/forms"; import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; -import { IDiscoverModel } from "../../interfaces"; +import { RequestType } from "../../interfaces"; import { SearchV2Service } from "../../services"; - +import { AdvancedSearchDialogDataService } from "./advanced-search-dialog-data.service"; @Component({ selector: "advanced-search-dialog", @@ -12,15 +12,14 @@ import { SearchV2Service } from "../../services"; }) export class AdvancedSearchDialogComponent implements OnInit { constructor( - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any, + public dialogRef: MatDialogRef, private fb: FormBuilder, private searchService: SearchV2Service, + private advancedSearchDialogService: AdvancedSearchDialogDataService ) {} public form: FormGroup; - public async ngOnInit() { this.form = this.fb.group({ @@ -35,17 +34,28 @@ export class AdvancedSearchDialogComponent implements OnInit { this.form.controls.genres.setValue([]); this.form.controls.watchProviders.setValue([]); }); - - } public async onSubmit() { - const watchProviderIds = this.form.controls.watchProviders.value.map(x => x.provider_id); - const genres = this.form.controls.genreIds.value.map(x => x.id); - await this.searchService.advancedSearch({ + const formData = this.form.value; + const watchProviderIds = formData.watchProviders.map(x => x.provider_id); + const genres = formData.genreIds.map(x => x.id); + const keywords = formData.keywordIds.map(x => x.id); + const data = await this.searchService.advancedSearch({ watchProviders: watchProviderIds, genreIds: genres, - type: this.form.controls.type.value, + keywordIds: keywords, + releaseYear: formData.releaseYear, + type: formData.type, }, 0, 30); + + this.advancedSearchDialogService.setData(data, formData.type === 'movie' ? RequestType.movie : RequestType.tvShow); + + this.dialogRef.close(true); + } + + public onClose() { + this.dialogRef.close(false); } + } diff --git a/src/Ombi/ClientApp/src/app/shared/components/genre-select/genre-select.component.html b/src/Ombi/ClientApp/src/app/shared/components/genre-select/genre-select.component.html index 9907b002c..93ed4618e 100644 --- a/src/Ombi/ClientApp/src/app/shared/components/genre-select/genre-select.component.html +++ b/src/Ombi/ClientApp/src/app/shared/components/genre-select/genre-select.component.html @@ -1,5 +1,5 @@ - + Genres - - - - {{option.name}} - - - --> - - - + Keywords - Watch Providers + + Watch Providers