Added the ability to filter the main search bar

pull/3798/head
tidusjar 4 years ago
parent 1eae6647db
commit 7ba63a33ce

@ -8,6 +8,6 @@ namespace Ombi.Core.Engine.V2
{ {
public interface IMultiSearchEngine public interface IMultiSearchEngine
{ {
Task<List<MultiSearchResult>> MultiSearch(string searchTerm, CancellationToken cancellationToken); Task<List<MultiSearchResult>> MultiSearch(string searchTerm, MultiSearchFilter filter, CancellationToken cancellationToken);
} }
} }

@ -36,7 +36,7 @@ namespace Ombi.Core.Engine.V2
private readonly IMusicBrainzApi _musicApi; private readonly IMusicBrainzApi _musicApi;
public async Task<List<MultiSearchResult>> MultiSearch(string searchTerm, CancellationToken cancellationToken) public async Task<List<MultiSearchResult>> MultiSearch(string searchTerm, MultiSearchFilter filter, CancellationToken cancellationToken)
{ {
var lang = await DefaultLanguageCode(null); var lang = await DefaultLanguageCode(null);
var model = new List<MultiSearchResult>(); var model = new List<MultiSearchResult>();
@ -44,7 +44,7 @@ namespace Ombi.Core.Engine.V2
var movieDbData = (await _movieDbApi.MultiSearch(searchTerm, lang, cancellationToken)).results; var movieDbData = (await _movieDbApi.MultiSearch(searchTerm, lang, cancellationToken)).results;
var lidarrSettings = await _lidarrSettings.GetSettingsAsync(); var lidarrSettings = await _lidarrSettings.GetSettingsAsync();
if (lidarrSettings.Enabled) if (lidarrSettings.Enabled && filter.Music)
{ {
var artistResult = await _musicApi.SearchArtist(searchTerm); var artistResult = await _musicApi.SearchArtist(searchTerm);
foreach (var artist in artistResult) foreach (var artist in artistResult)
@ -66,7 +66,7 @@ namespace Ombi.Core.Engine.V2
Poster = multiSearch.poster_path Poster = multiSearch.poster_path
}; };
if (multiSearch.media_type.Equals("movie", StringComparison.InvariantCultureIgnoreCase)) if (multiSearch.media_type.Equals("movie", StringComparison.InvariantCultureIgnoreCase) && filter.Movies)
{ {
if (multiSearch.release_date.HasValue() && DateTime.TryParse(multiSearch.release_date, out var releaseDate)) if (multiSearch.release_date.HasValue() && DateTime.TryParse(multiSearch.release_date, out var releaseDate))
{ {
@ -78,7 +78,7 @@ namespace Ombi.Core.Engine.V2
} }
} }
if (multiSearch.media_type.Equals("tv", StringComparison.InvariantCultureIgnoreCase)) else if (multiSearch.media_type.Equals("tv", StringComparison.InvariantCultureIgnoreCase) && filter.TvShows)
{ {
if (multiSearch.release_date.HasValue() && DateTime.TryParse(multiSearch.release_date, out var releaseDate)) if (multiSearch.release_date.HasValue() && DateTime.TryParse(multiSearch.release_date, out var releaseDate))
{ {
@ -89,11 +89,14 @@ namespace Ombi.Core.Engine.V2
result.Title = multiSearch.name; result.Title = multiSearch.name;
} }
} }
else if (multiSearch.media_type.Equals("person", StringComparison.InvariantCultureIgnoreCase) && filter.People)
if (multiSearch.media_type.Equals("person", StringComparison.InvariantCultureIgnoreCase))
{ {
result.Title = multiSearch.name; result.Title = multiSearch.name;
} }
else
{
continue;
}
result.Id = multiSearch.id.ToString(); result.Id = multiSearch.id.ToString();
model.Add(result); model.Add(result);

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Ombi.Core.Models.Search.V2
{
public class MultiSearchFilter
{
public bool Movies { get; set; }
public bool TvShows { get; set; }
public bool Music { get; set; }
public bool People { get; set; }
}
}

@ -64,6 +64,7 @@ import { NavSearchComponent } from "./my-nav/nav-search.component";
import { OverlayModule } from "@angular/cdk/overlay"; import { OverlayModule } from "@angular/cdk/overlay";
import { StorageService } from "./shared/storage/storage-service"; import { StorageService } from "./shared/storage/storage-service";
import { SignalRNotificationService } from "./services/signlarnotification.service"; import { SignalRNotificationService } from "./services/signlarnotification.service";
import { MatMenuModule } from "@angular/material/menu";
const routes: Routes = [ const routes: Routes = [
{ path: "*", component: PageNotFoundComponent }, { path: "*", component: PageNotFoundComponent },
{ path: "", redirectTo: "/discover", pathMatch: "full" }, { path: "", redirectTo: "/discover", pathMatch: "full" },
@ -126,6 +127,7 @@ export function JwtTokenGetter() {
NavbarModule, NavbarModule,
MatCardModule, MatCardModule,
MatTooltipModule, MatTooltipModule,
MatMenuModule,
MatInputModule, MatInputModule,
MatTabsModule, MatTabsModule,
ReactiveFormsModule, ReactiveFormsModule,

@ -0,0 +1,7 @@
export class SearchFilter {
movies: boolean;
tvShows: boolean;
music: boolean;
people: boolean;
}

@ -49,17 +49,28 @@
<mat-icon aria-label="Side nav toggle icon">menu</mat-icon> <mat-icon aria-label="Side nav toggle icon">menu</mat-icon>
</button> </button>
<div class="col-10"> <div class="col-8 col-lg-10">
<span class="justify-content-center align-items-center"> <span class="justify-content-center align-items-center">
<!-- Search Bar --> <!-- Search Bar -->
<div> <div>
<app-nav-search></app-nav-search> <app-nav-search [filter]="searchFilter"></app-nav-search>
</div> </div>
</span> </span>
</div> </div>
<div class="col-2">
<button mat-icon-button [matMenuTriggerFor]="filterMenu"><mat-icon>filter_alt</mat-icon></button>
<mat-menu #filterMenu="matMenu" yPosition="below" class="smaller-panel">
<mat-slide-toggle class="mat-menu-item slide-menu" [checked]="searchFilter.movies" (click)="$event.stopPropagation()" (change)="changeFilter($event,SearchFilterType.Movie)">{{ 'NavigationBar.Filter.Movies' | translate}}</mat-slide-toggle>
<mat-slide-toggle class="mat-menu-item slide-menu" [checked]="searchFilter.tvShows" (click)="$event.stopPropagation()" (change)="changeFilter($event,SearchFilterType.TvShow)">{{ 'NavigationBar.Filter.TvShows' | translate}}</mat-slide-toggle>
<mat-slide-toggle class="mat-menu-item slide-menu" [checked]="searchFilter.music" (click)="$event.stopPropagation()" (change)="changeFilter($event,SearchFilterType.Music)">{{ 'NavigationBar.Filter.Music' | translate}}</mat-slide-toggle>
<mat-slide-toggle class="mat-menu-item slide-menu" [checked]="searchFilter.people" (click)="$event.stopPropagation()" (change)="changeFilter($event,SearchFilterType.People)">{{ 'NavigationBar.Filter.People' | translate}}</mat-slide-toggle>
</mat-menu>
</div>
</mat-toolbar> </mat-toolbar>
<!-- Page --> <!-- Page -->
<ng-container *ngTemplateOutlet="template"></ng-container> <ng-container *ngTemplateOutlet="template"></ng-container>

@ -55,6 +55,13 @@
font-weight:500; font-weight:500;
} }
.slide-menu {
width: 100%;
}
::ng-deep .smaller-panel {
max-width: 170px !important;
}
.mat-drawer-content { .mat-drawer-content {
position: relative; position: relative;

@ -5,6 +5,15 @@ import { map } from 'rxjs/operators';
import { INavBar } from '../interfaces/ICommon'; import { INavBar } from '../interfaces/ICommon';
import { StorageService } from '../shared/storage/storage-service'; import { StorageService } from '../shared/storage/storage-service';
import { SettingsService } from '../services'; import { SettingsService } from '../services';
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
import { SearchFilter } from './SearchFilter';
export enum SearchFilterType {
Movie = 1,
TvShow = 2,
Music = 3,
People = 4
}
@Component({ @Component({
selector: 'app-my-nav', selector: 'app-my-nav',
@ -27,6 +36,8 @@ export class MyNavComponent implements OnInit {
public theme: string; public theme: string;
public issuesEnabled: boolean = false; public issuesEnabled: boolean = false;
public navItems: INavBar[]; public navItems: INavBar[];
public searchFilter: SearchFilter;
public SearchFilterType = SearchFilterType;
constructor(private breakpointObserver: BreakpointObserver, constructor(private breakpointObserver: BreakpointObserver,
private settingsService: SettingsService, private settingsService: SettingsService,
@ -35,12 +46,23 @@ export class MyNavComponent implements OnInit {
public async ngOnInit() { public async ngOnInit() {
this.searchFilter = {
movies: true,
music: false,
people: false,
tvShows: true
}
this.issuesEnabled = await this.settingsService.issueEnabled().toPromise(); this.issuesEnabled = await this.settingsService.issueEnabled().toPromise();
const customizationSettings = await this.settingsService.getCustomization().toPromise(); const customizationSettings = await this.settingsService.getCustomization().toPromise();
console.log("issues enabled: " + this.issuesEnabled); console.log("issues enabled: " + this.issuesEnabled);
this.theme = this.store.get("theme"); this.theme = this.store.get("theme");
if(!this.theme) { if (!this.theme) {
this.store.save("theme","dark"); this.store.save("theme", "dark");
}
var filter = this.store.get("searchFilter");
if (filter) {
this.searchFilter = Object.assign(new SearchFilter(), JSON.parse(filter));
} }
this.navItems = [ this.navItems = [
{ name: "NavigationBar.Discover", icon: "find_replace", link: "/discover", requiresAdmin: false, enabled: true, faIcon: null }, { name: "NavigationBar.Discover", icon: "find_replace", link: "/discover", requiresAdmin: false, enabled: true, faIcon: null },
@ -73,4 +95,22 @@ export class MyNavComponent implements OnInit {
this.themeChange.emit(newTheme); this.themeChange.emit(newTheme);
} }
} }
public changeFilter(event: MatSlideToggleChange, searchFilterType: SearchFilterType) {
switch (searchFilterType) {
case SearchFilterType.Movie:
this.searchFilter.movies = event.checked;
break;
case SearchFilterType.TvShow:
this.searchFilter.tvShows = event.checked;
break;
case SearchFilterType.Music:
this.searchFilter.music = event.checked;
break;
case SearchFilterType.People:
this.searchFilter.people = event.checked;
break;
}
this.store.save("searchFilter", JSON.stringify(this.searchFilter));
}
} }

@ -1,4 +1,4 @@
import { Component, OnInit } from "@angular/core"; import { Component, Input, OnInit } from "@angular/core";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { import {
debounceTime, debounceTime,
@ -14,6 +14,7 @@ import { IMultiSearchResult } from "../interfaces";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { FormGroup, FormBuilder } from "@angular/forms"; import { FormGroup, FormBuilder } from "@angular/forms";
import { MatAutocompleteSelectedEvent } from "@angular/material/autocomplete"; import { MatAutocompleteSelectedEvent } from "@angular/material/autocomplete";
import { SearchFilter } from "./SearchFilter";
@Component({ @Component({
selector: "app-nav-search", selector: "app-nav-search",
@ -21,6 +22,7 @@ import { MatAutocompleteSelectedEvent } from "@angular/material/autocomplete";
styleUrls: ["./nav-search.component.scss"], styleUrls: ["./nav-search.component.scss"],
}) })
export class NavSearchComponent implements OnInit { export class NavSearchComponent implements OnInit {
@Input() public filter: SearchFilter;
public selectedItem: string; public selectedItem: string;
public results: IMultiSearchResult[]; public results: IMultiSearchResult[];
public searching = false; public searching = false;
@ -46,7 +48,7 @@ export class NavSearchComponent implements OnInit {
switchMap((value: string) => { switchMap((value: string) => {
if (value) { if (value) {
return this.searchService return this.searchService
.multiSearch(value) .multiSearch(value, this.filter)
.pipe(finalize(() => (this.searching = false))); .pipe(finalize(() => (this.searching = false)));
} }
return empty().pipe(finalize(() => (this.searching = false))); return empty().pipe(finalize(() => (this.searching = false)));

@ -10,6 +10,7 @@ import { ServiceHelpers } from "./service.helpers";
import { ISearchMovieResultV2 } from "../interfaces/ISearchMovieResultV2"; import { ISearchMovieResultV2 } from "../interfaces/ISearchMovieResultV2";
import { ISearchTvResultV2, IMovieCollectionsViewModel, IActorCredits } from "../interfaces/ISearchTvResultV2"; import { ISearchTvResultV2, IMovieCollectionsViewModel, IActorCredits } from "../interfaces/ISearchTvResultV2";
import { IArtistSearchResult, IAlbumArt } from "../interfaces/IMusicSearchResultV2"; import { IArtistSearchResult, IAlbumArt } from "../interfaces/IMusicSearchResultV2";
import { SearchFilter } from "../my-nav/SearchFilter";
@Injectable() @Injectable()
export class SearchV2Service extends ServiceHelpers { export class SearchV2Service extends ServiceHelpers {
@ -17,8 +18,8 @@ export class SearchV2Service extends ServiceHelpers {
super(http, "/api/v2/search", href); super(http, "/api/v2/search", href);
} }
public multiSearch(searchTerm: string): Observable<IMultiSearchResult[]> { public multiSearch(searchTerm: string, filter: SearchFilter): Observable<IMultiSearchResult[]> {
return this.http.get<IMultiSearchResult[]>(`${this.url}/multi/${searchTerm}`); return this.http.post<IMultiSearchResult[]>(`${this.url}/multi/${searchTerm}`, filter);
} }
public getFullMovieDetails(theMovieDbId: number): Observable<ISearchMovieResultV2> { public getFullMovieDetails(theMovieDbId: number): Observable<ISearchMovieResultV2> {
return this.http.get<ISearchMovieResultV2>(`${this.url}/Movie/${theMovieDbId}`); return this.http.get<ISearchMovieResultV2>(`${this.url}/Movie/${theMovieDbId}`);

@ -21,6 +21,7 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatSortModule } from '@angular/material/sort'; import { MatSortModule } from '@angular/material/sort';
import { MatStepperModule } from '@angular/material/stepper'; import { MatStepperModule } from '@angular/material/stepper';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import {MatMenuModule} from '@angular/material/menu';
import { MatToolbarModule } from '@angular/material/toolbar'; import { MatToolbarModule } from '@angular/material/toolbar';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { MatTreeModule } from '@angular/material/tree'; import { MatTreeModule } from '@angular/material/tree';
@ -57,6 +58,7 @@ import { EpisodeRequestComponent } from "./episode-request/episode-request.compo
MatNativeDateModule, MatNativeDateModule,
MatChipsModule, MatChipsModule,
MatIconModule, MatIconModule,
MatMenuModule,
MatSidenavModule, MatSidenavModule,
MatListModule, MatListModule,
MatToolbarModule, MatToolbarModule,
@ -71,7 +73,6 @@ import { EpisodeRequestComponent } from "./episode-request/episode-request.compo
MatTreeModule, MatTreeModule,
MatStepperModule, MatStepperModule,
MatSnackBarModule, MatSnackBarModule,
MatSlideToggleModule,
], ],
entryComponents: [ entryComponents: [
EpisodeRequestComponent EpisodeRequestComponent
@ -95,6 +96,7 @@ import { EpisodeRequestComponent } from "./episode-request/episode-request.compo
MatButtonModule, MatButtonModule,
MatNativeDateModule, MatNativeDateModule,
MatIconModule, MatIconModule,
MatMenuModule,
MatSnackBarModule, MatSnackBarModule,
MatSidenavModule, MatSidenavModule,
MatSelectModule, MatSelectModule,

@ -44,11 +44,12 @@ namespace Ombi.Controllers.V2
/// Show information using the MovieDbId.</remarks> /// Show information using the MovieDbId.</remarks>
/// <param name="searchTerm">The search you want, this can be for a movie or TV show e.g. Star Wars will return /// <param name="searchTerm">The search you want, this can be for a movie or TV show e.g. Star Wars will return
/// all Star Wars movies and Star Wars Rebels the TV Sho</param> /// all Star Wars movies and Star Wars Rebels the TV Sho</param>
/// <param name="filter">Filter for the search</param>
/// <returns></returns> /// <returns></returns>
[HttpGet("multi/{searchTerm}")] [HttpPost("multi/{searchTerm}")]
public async Task<List<MultiSearchResult>> MultiSearch(string searchTerm) public async Task<List<MultiSearchResult>> MultiSearch(string searchTerm, [FromBody] MultiSearchFilter filter)
{ {
return await _multiSearchEngine.MultiSearch(searchTerm, Request.HttpContext.RequestAborted); return await _multiSearchEngine.MultiSearch(searchTerm, filter, Request.HttpContext.RequestAborted);
} }
/// <summary> /// <summary>

@ -69,7 +69,13 @@
"Calendar": "Calendar", "Calendar": "Calendar",
"UserPreferences": "Preferences", "UserPreferences": "Preferences",
"FeatureSuggestion":"Feature Suggestion", "FeatureSuggestion":"Feature Suggestion",
"FeatureSuggestionTooltip":"Have a great new idea? Suggest it here!" "FeatureSuggestionTooltip":"Have a great new idea? Suggest it here!",
"Filter": {
"Movies":"Movies",
"TvShows":"TV Shows",
"Music":"Music",
"People":"People"
}
}, },
"Search": { "Search": {
"Title": "Search", "Title": "Search",

Loading…
Cancel
Save