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
{
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;
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 model = new List<MultiSearchResult>();
@ -44,7 +44,7 @@ namespace Ombi.Core.Engine.V2
var movieDbData = (await _movieDbApi.MultiSearch(searchTerm, lang, cancellationToken)).results;
var lidarrSettings = await _lidarrSettings.GetSettingsAsync();
if (lidarrSettings.Enabled)
if (lidarrSettings.Enabled && filter.Music)
{
var artistResult = await _musicApi.SearchArtist(searchTerm);
foreach (var artist in artistResult)
@ -66,7 +66,7 @@ namespace Ombi.Core.Engine.V2
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))
{
@ -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))
{
@ -89,11 +89,14 @@ namespace Ombi.Core.Engine.V2
result.Title = multiSearch.name;
}
}
if (multiSearch.media_type.Equals("person", StringComparison.InvariantCultureIgnoreCase))
else if (multiSearch.media_type.Equals("person", StringComparison.InvariantCultureIgnoreCase) && filter.People)
{
result.Title = multiSearch.name;
}
else
{
continue;
}
result.Id = multiSearch.id.ToString();
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 { StorageService } from "./shared/storage/storage-service";
import { SignalRNotificationService } from "./services/signlarnotification.service";
import { MatMenuModule } from "@angular/material/menu";
const routes: Routes = [
{ path: "*", component: PageNotFoundComponent },
{ path: "", redirectTo: "/discover", pathMatch: "full" },
@ -126,6 +127,7 @@ export function JwtTokenGetter() {
NavbarModule,
MatCardModule,
MatTooltipModule,
MatMenuModule,
MatInputModule,
MatTabsModule,
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>
</button>
<div class="col-10">
<div class="col-8 col-lg-10">
<span class="justify-content-center align-items-center">
<!-- Search Bar -->
<div>
<app-nav-search></app-nav-search>
<app-nav-search [filter]="searchFilter"></app-nav-search>
</div>
</span>
</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>
<!-- Page -->
<ng-container *ngTemplateOutlet="template"></ng-container>

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

@ -5,6 +5,15 @@ import { map } from 'rxjs/operators';
import { INavBar } from '../interfaces/ICommon';
import { StorageService } from '../shared/storage/storage-service';
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({
selector: 'app-my-nav',
@ -27,20 +36,33 @@ export class MyNavComponent implements OnInit {
public theme: string;
public issuesEnabled: boolean = false;
public navItems: INavBar[];
public searchFilter: SearchFilter;
public SearchFilterType = SearchFilterType;
constructor(private breakpointObserver: BreakpointObserver,
private settingsService: SettingsService,
private store: StorageService) {
private settingsService: SettingsService,
private store: StorageService) {
}
public async ngOnInit() {
this.searchFilter = {
movies: true,
music: false,
people: false,
tvShows: true
}
this.issuesEnabled = await this.settingsService.issueEnabled().toPromise();
const customizationSettings = await this.settingsService.getCustomization().toPromise();
console.log("issues enabled: " + this.issuesEnabled);
this.theme = this.store.get("theme");
if(!this.theme) {
this.store.save("theme","dark");
if (!this.theme) {
this.store.save("theme", "dark");
}
var filter = this.store.get("searchFilter");
if (filter) {
this.searchFilter = Object.assign(new SearchFilter(), JSON.parse(filter));
}
this.navItems = [
{ 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);
}
}
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 {
debounceTime,
@ -14,6 +14,7 @@ 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";
@Component({
selector: "app-nav-search",
@ -21,6 +22,7 @@ import { MatAutocompleteSelectedEvent } from "@angular/material/autocomplete";
styleUrls: ["./nav-search.component.scss"],
})
export class NavSearchComponent implements OnInit {
@Input() public filter: SearchFilter;
public selectedItem: string;
public results: IMultiSearchResult[];
public searching = false;
@ -46,7 +48,7 @@ export class NavSearchComponent implements OnInit {
switchMap((value: string) => {
if (value) {
return this.searchService
.multiSearch(value)
.multiSearch(value, this.filter)
.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 { ISearchTvResultV2, IMovieCollectionsViewModel, IActorCredits } from "../interfaces/ISearchTvResultV2";
import { IArtistSearchResult, IAlbumArt } from "../interfaces/IMusicSearchResultV2";
import { SearchFilter } from "../my-nav/SearchFilter";
@Injectable()
export class SearchV2Service extends ServiceHelpers {
@ -17,8 +18,8 @@ export class SearchV2Service extends ServiceHelpers {
super(http, "/api/v2/search", href);
}
public multiSearch(searchTerm: string): Observable<IMultiSearchResult[]> {
return this.http.get<IMultiSearchResult[]>(`${this.url}/multi/${searchTerm}`);
public multiSearch(searchTerm: string, filter: SearchFilter): Observable<IMultiSearchResult[]> {
return this.http.post<IMultiSearchResult[]>(`${this.url}/multi/${searchTerm}`, filter);
}
public getFullMovieDetails(theMovieDbId: number): Observable<ISearchMovieResultV2> {
return this.http.get<ISearchMovieResultV2>(`${this.url}/Movie/${theMovieDbId}`);
@ -27,7 +28,7 @@ export class SearchV2Service extends ServiceHelpers {
public getMovieByImdbId(imdbId: string): Observable<ISearchMovieResultV2> {
return this.http.get<ISearchMovieResultV2>(`${this.url}/Movie/imdb/${imdbId}`);
}
public getFullMovieDetailsByRequestId(requestId: number): Promise<ISearchMovieResultV2> {
return this.http.get<ISearchMovieResultV2>(`${this.url}/Movie/request/${requestId}`).toPromise();
}
@ -35,7 +36,7 @@ export class SearchV2Service extends ServiceHelpers {
public getFullMovieDetailsPromise(theMovieDbId: number): Promise<ISearchMovieResultV2> {
return this.http.get<ISearchMovieResultV2>(`${this.url}/Movie/${theMovieDbId}`).toPromise();
}
public similarMovies(theMovieDbId: number, langCode: string): Observable<ISearchMovieResult[]> {
return this.http.post<ISearchMovieResult[]>(`${this.url}/Movie/similar`, {theMovieDbId, languageCode: langCode});
}
@ -62,11 +63,11 @@ export class SearchV2Service extends ServiceHelpers {
public nowPlayingMoviesByPage(currentlyLoaded: number, toLoad: number): Promise<ISearchMovieResult[]> {
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/nowplaying/${currentlyLoaded}/${toLoad}`).toPromise();
}
public topRatedMovies(): Observable<ISearchMovieResult[]> {
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/toprated`);
}
public popularTv(): Observable<ISearchTvResult[]> {
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/popular`, { headers: this.headers });
}
@ -84,7 +85,7 @@ export class SearchV2Service extends ServiceHelpers {
public anticipatedTvByPage(currentlyLoaded: number, toLoad: number): Promise<ISearchTvResult[]> {
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/anticipated/${currentlyLoaded}/${toLoad}`, { headers: this.headers }).toPromise();
}
public trendingTv(): Observable<ISearchTvResult[]> {
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/trending`, { headers: this.headers });
}
@ -92,7 +93,7 @@ export class SearchV2Service extends ServiceHelpers {
public trendingTvByPage(currentlyLoaded: number, toLoad: number): Promise<ISearchTvResult[]> {
return this.http.get<ISearchTvResult[]>(`${this.url}/Tv/trending/${currentlyLoaded}/${toLoad}`, { headers: this.headers }).toPromise();
}
public getTvInfo(tvdbid: number): Promise<ISearchTvResultV2> {
return this.http.get<ISearchTvResultV2>(`${this.url}/Tv/${tvdbid}`, { headers: this.headers }).toPromise();
}
@ -100,11 +101,11 @@ export class SearchV2Service extends ServiceHelpers {
public getTvInfoWithRequestId(requestId: number): Promise<ISearchTvResultV2> {
return this.http.get<ISearchTvResultV2>(`${this.url}/Tv/request/${requestId}`, { headers: this.headers }).toPromise();
}
public getTvInfoWithMovieDbId(theMovieDbId: number): Promise<ISearchTvResultV2> {
return this.http.get<ISearchTvResultV2>(`${this.url}/Tv/moviedb/${theMovieDbId}`, { headers: this.headers }).toPromise();
}
public getMovieCollections(collectionId: number): Promise<IMovieCollectionsViewModel> {
return this.http.get<IMovieCollectionsViewModel>(`${this.url}/movie/collection/${collectionId}`, { headers: this.headers }).toPromise();
}

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

@ -44,11 +44,12 @@ namespace Ombi.Controllers.V2
/// 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
/// all Star Wars movies and Star Wars Rebels the TV Sho</param>
/// <param name="filter">Filter for the search</param>
/// <returns></returns>
[HttpGet("multi/{searchTerm}")]
public async Task<List<MultiSearchResult>> MultiSearch(string searchTerm)
[HttpPost("multi/{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>

@ -69,7 +69,13 @@
"Calendar": "Calendar",
"UserPreferences": "Preferences",
"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": {
"Title": "Search",

Loading…
Cancel
Save