mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-19 12:59:39 -07:00
MORE!
This commit is contained in:
parent
4118f0de41
commit
6ee9606f7c
23 changed files with 314 additions and 29 deletions
|
@ -29,5 +29,6 @@ namespace Ombi.Core.Engine.Interfaces
|
||||||
Task<IEnumerable<StreamingData>> GetStreamInformation(int movieDbId, CancellationToken cancellationToken);
|
Task<IEnumerable<StreamingData>> GetStreamInformation(int movieDbId, CancellationToken cancellationToken);
|
||||||
Task<IEnumerable<SearchMovieViewModel>> RecentlyRequestedMovies(int currentlyLoaded, int toLoad, CancellationToken cancellationToken);
|
Task<IEnumerable<SearchMovieViewModel>> RecentlyRequestedMovies(int currentlyLoaded, int toLoad, CancellationToken cancellationToken);
|
||||||
Task<IEnumerable<SearchMovieViewModel>> SeasonalList(int currentPosition, int amountToLoad, CancellationToken cancellationToken);
|
Task<IEnumerable<SearchMovieViewModel>> SeasonalList(int currentPosition, int amountToLoad, CancellationToken cancellationToken);
|
||||||
|
Task<IEnumerable<SearchMovieViewModel>> AdvancedSearch(DiscoverModel model, int currentlyLoaded, int toLoad, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -137,7 +137,22 @@ namespace Ombi.Core.Engine.V2
|
||||||
foreach (var pagesToLoad in pages)
|
foreach (var pagesToLoad in pages)
|
||||||
{
|
{
|
||||||
var apiResult = await Cache.GetOrAddAsync(nameof(PopularMovies) + pagesToLoad.Page + langCode,
|
var apiResult = await Cache.GetOrAddAsync(nameof(PopularMovies) + pagesToLoad.Page + langCode,
|
||||||
() => MovieApi.PopularMovies(langCode, pagesToLoad.Page, cancellationToken), DateTimeOffset.Now.AddHours(12));
|
() => MovieApi.PopularMovies(langCode, pagesToLoad.Page, cancellationToken), DateTimeOffset.Now.AddHours(12));
|
||||||
|
results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take));
|
||||||
|
}
|
||||||
|
return await TransformMovieResultsToResponse(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<SearchMovieViewModel>> AdvancedSearch(DiscoverModel model, int currentlyLoaded, int toLoad, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var langCode = await DefaultLanguageCode(null);
|
||||||
|
|
||||||
|
var pages = PaginationHelper.GetNextPages(currentlyLoaded, toLoad, _theMovieDbMaxPageItems);
|
||||||
|
|
||||||
|
var results = new List<MovieDbSearchResult>();
|
||||||
|
foreach (var pagesToLoad in pages)
|
||||||
|
{
|
||||||
|
var apiResult = await MovieApi.AdvancedSearch(model, cancellationToken);
|
||||||
results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take));
|
results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take));
|
||||||
}
|
}
|
||||||
return await TransformMovieResultsToResponse(results);
|
return await TransformMovieResultsToResponse(results);
|
||||||
|
|
|
@ -40,5 +40,7 @@ namespace Ombi.Api.TheMovieDb
|
||||||
Task<WatchProviders> GetMovieWatchProviders(int theMoviedbId, CancellationToken token);
|
Task<WatchProviders> GetMovieWatchProviders(int theMoviedbId, CancellationToken token);
|
||||||
Task<WatchProviders> GetTvWatchProviders(int theMoviedbId, CancellationToken token);
|
Task<WatchProviders> GetTvWatchProviders(int theMoviedbId, CancellationToken token);
|
||||||
Task<List<Genre>> GetGenres(string media, CancellationToken cancellationToken);
|
Task<List<Genre>> GetGenres(string media, CancellationToken cancellationToken);
|
||||||
|
Task<List<WatchProvidersResults>> SearchWatchProviders(string media, string searchTerm, CancellationToken cancellationToken);
|
||||||
|
Task<List<MovieDbSearchResult>> AdvancedSearch(DiscoverModel model, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
18
src/Ombi.TheMovieDbApi/Models/DiscoverModel.cs
Normal file
18
src/Ombi.TheMovieDbApi/Models/DiscoverModel.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ombi.Api.TheMovieDb.Models
|
||||||
|
{
|
||||||
|
public class DiscoverModel
|
||||||
|
{
|
||||||
|
public string Type { get; set; }
|
||||||
|
public int? ReleaseYear { get; set; }
|
||||||
|
public List<int> GenreIds { get; set; } = new List<int>();
|
||||||
|
public List<int> KeywordIds { get; set; } = new List<int>();
|
||||||
|
public List<int> WatchProviders { get; set; } = new List<int>();
|
||||||
|
public List<int> Companies { get; set; } = new List<int>();
|
||||||
|
}
|
||||||
|
}
|
21
src/Ombi.TheMovieDbApi/Models/DiscoverTv.cs
Normal file
21
src/Ombi.TheMovieDbApi/Models/DiscoverTv.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
namespace Ombi.Api.TheMovieDb.Models {
|
||||||
|
|
||||||
|
public class DiscoverTv
|
||||||
|
{
|
||||||
|
public int vote_count { get; set; }
|
||||||
|
public int id { get; set; }
|
||||||
|
public bool video { get; set; }
|
||||||
|
public float vote_average { get; set; }
|
||||||
|
public string title { get; set; }
|
||||||
|
public float popularity { get; set; }
|
||||||
|
public string poster_path { get; set; }
|
||||||
|
public string original_language { get; set; }
|
||||||
|
public string original_title { get; set; }
|
||||||
|
public int[] genre_ids { get; set; }
|
||||||
|
public string backdrop_path { get; set; }
|
||||||
|
public bool adult { get; set; }
|
||||||
|
public string overview { get; set; }
|
||||||
|
public string release_date { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
16
src/Ombi.TheMovieDbApi/Models/WatchProvidersResults.cs
Normal file
16
src/Ombi.TheMovieDbApi/Models/WatchProvidersResults.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ombi.Api.TheMovieDb.Models
|
||||||
|
{
|
||||||
|
public class WatchProvidersResults
|
||||||
|
{
|
||||||
|
public int provider_id { get; set; }
|
||||||
|
public string logo_path { get; set; }
|
||||||
|
public string provider_name { get; set; }
|
||||||
|
public string origin_country { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,6 +68,34 @@ namespace Ombi.Api.TheMovieDb
|
||||||
return await Api.Request<TheMovieDbContainer<DiscoverMovies>>(request);
|
return await Api.Request<TheMovieDbContainer<DiscoverMovies>>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<List<MovieDbSearchResult>> AdvancedSearch(DiscoverModel model, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var request = new Request($"discover/{model.Type}", BaseUri, HttpMethod.Get);
|
||||||
|
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||||
|
if(model.ReleaseYear.HasValue && model.ReleaseYear.Value > 1900)
|
||||||
|
{
|
||||||
|
request.FullUri = request.FullUri.AddQueryParameter("year", model.ReleaseYear.Value.ToString());
|
||||||
|
}
|
||||||
|
if (model.KeywordIds.Any())
|
||||||
|
{
|
||||||
|
request.FullUri = request.FullUri.AddQueryParameter("with_keyword", string.Join(',', model.KeywordIds));
|
||||||
|
}
|
||||||
|
if (model.GenreIds.Any())
|
||||||
|
{
|
||||||
|
request.FullUri = request.FullUri.AddQueryParameter("with_genres", string.Join(',', model.GenreIds));
|
||||||
|
}
|
||||||
|
if (model.WatchProviders.Any())
|
||||||
|
{
|
||||||
|
request.FullUri = request.FullUri.AddQueryParameter("with_watch_providers", string.Join(',', model.WatchProviders));
|
||||||
|
}
|
||||||
|
request.FullUri = request.FullUri.AddQueryParameter("sort_by", "popularity.desc");
|
||||||
|
|
||||||
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request, cancellationToken);
|
||||||
|
return Mapper.Map<List<MovieDbSearchResult>>(result.results);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Collections> GetCollection(string langCode, int collectionId, CancellationToken cancellationToken)
|
public async Task<Collections> GetCollection(string langCode, int collectionId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// https://developers.themoviedb.org/3/discover/movie-discover
|
// https://developers.themoviedb.org/3/discover/movie-discover
|
||||||
|
@ -368,6 +396,17 @@ namespace Ombi.Api.TheMovieDb
|
||||||
return result.results ?? new List<TheMovidDbKeyValue>();
|
return result.results ?? new List<TheMovidDbKeyValue>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<WatchProvidersResults>> SearchWatchProviders(string media, string searchTerm, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var request = new Request($"/watch/providers/{media}", BaseUri, HttpMethod.Get);
|
||||||
|
request.AddQueryString("api_key", ApiToken);
|
||||||
|
request.AddQueryString("query", searchTerm);
|
||||||
|
AddRetry(request);
|
||||||
|
|
||||||
|
var result = await Api.Request<TheMovieDbContainer<WatchProvidersResults>>(request, cancellationToken);
|
||||||
|
return result.results ?? new List<WatchProvidersResults>();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<TheMovidDbKeyValue> GetKeyword(int keywordId)
|
public async Task<TheMovidDbKeyValue> GetKeyword(int keywordId)
|
||||||
{
|
{
|
||||||
var request = new Request($"keyword/{keywordId}", BaseUri, HttpMethod.Get);
|
var request = new Request($"keyword/{keywordId}", BaseUri, HttpMethod.Get);
|
||||||
|
|
|
@ -2,3 +2,18 @@
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IWatchProvidersResults {
|
||||||
|
provider_id: number;
|
||||||
|
logo_path: string;
|
||||||
|
provider_name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDiscoverModel {
|
||||||
|
type: string;
|
||||||
|
releaseYear?: number|undefined;
|
||||||
|
genreIds?: number[];
|
||||||
|
keywordIds?: number[];
|
||||||
|
watchProviders?: number[];
|
||||||
|
companies?: number[];
|
||||||
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@
|
||||||
<mat-slide-toggle id="filterMusic" class="mat-menu-item slide-menu" [checked]="searchFilter.music"
|
<mat-slide-toggle id="filterMusic" class="mat-menu-item slide-menu" [checked]="searchFilter.music"
|
||||||
(click)="$event.stopPropagation()" (change)="changeFilter($event,SearchFilterType.Music)">
|
(click)="$event.stopPropagation()" (change)="changeFilter($event,SearchFilterType.Music)">
|
||||||
{{ 'NavigationBar.Filter.Music' | translate}}</mat-slide-toggle>
|
{{ 'NavigationBar.Filter.Music' | translate}}</mat-slide-toggle>
|
||||||
<button (click)="openAdvancedSearch()">Advanced Search</button>
|
<button class="advanced-search" mat-raised-button color="accent" (click)="openAdvancedSearch()">Advanced Search</button>
|
||||||
<!-- <mat-slide-toggle class="mat-menu-item slide-menu" [checked]="searchFilter.people"
|
<!-- <mat-slide-toggle class="mat-menu-item slide-menu" [checked]="searchFilter.people"
|
||||||
(click)="$event.stopPropagation()" (change)="changeFilter($event,SearchFilterType.People)">
|
(click)="$event.stopPropagation()" (change)="changeFilter($event,SearchFilterType.People)">
|
||||||
{{ 'NavigationBar.Filter.People' | translate}}</mat-slide-toggle> -->
|
{{ 'NavigationBar.Filter.People' | translate}}</mat-slide-toggle> -->
|
||||||
|
|
|
@ -230,4 +230,8 @@
|
||||||
::ng-deep .mat-sidenav-fixed .mat-list-base .mat-list-item .mat-list-item-content, .mat-list-base .mat-list-option .mat-list-item-content{
|
::ng-deep .mat-sidenav-fixed .mat-list-base .mat-list-item .mat-list-item-content, .mat-list-base .mat-list-option .mat-list-item-content{
|
||||||
padding:0;
|
padding:0;
|
||||||
margin: 0 4em 0 0.5em;
|
margin: 0 4em 0 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.advanced-search {
|
||||||
|
margin-left: 10px;
|
||||||
}
|
}
|
|
@ -4,7 +4,7 @@ import { Injectable, Inject } from "@angular/core";
|
||||||
import { empty, Observable, throwError } from "rxjs";
|
import { empty, Observable, throwError } from "rxjs";
|
||||||
import { catchError } from "rxjs/operators";
|
import { catchError } from "rxjs/operators";
|
||||||
|
|
||||||
import { IMovieDbKeyword } from "../../interfaces";
|
import { IMovieDbKeyword, IWatchProvidersResults } from "../../interfaces";
|
||||||
import { ServiceHelpers } from "../service.helpers";
|
import { ServiceHelpers } from "../service.helpers";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
|
@ -28,4 +28,8 @@ export class TheMovieDbService extends ServiceHelpers {
|
||||||
public getGenres(media: string): Observable<IMovieDbKeyword[]> {
|
public getGenres(media: string): Observable<IMovieDbKeyword[]> {
|
||||||
return this.http.get<IMovieDbKeyword[]>(`${this.url}/Genres/${media}`, { headers: this.headers })
|
return this.http.get<IMovieDbKeyword[]>(`${this.url}/Genres/${media}`, { headers: this.headers })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getWatchProviders(media: string): Observable<IWatchProvidersResults[]> {
|
||||||
|
return this.http.get<IWatchProvidersResults[]>(`${this.url}/WatchProviders/${media}`, {headers: this.headers});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Injectable, Inject } from "@angular/core";
|
||||||
import { HttpClient } from "@angular/common/http";
|
import { HttpClient } from "@angular/common/http";
|
||||||
import { Observable } from "rxjs";
|
import { Observable } from "rxjs";
|
||||||
|
|
||||||
import { IMultiSearchResult, ISearchMovieResult, ISearchTvResult } from "../interfaces";
|
import { IDiscoverModel, IMultiSearchResult, ISearchMovieResult, ISearchTvResult } from "../interfaces";
|
||||||
import { ServiceHelpers } from "./service.helpers";
|
import { ServiceHelpers } from "./service.helpers";
|
||||||
|
|
||||||
import { ISearchMovieResultV2 } from "../interfaces/ISearchMovieResultV2";
|
import { ISearchMovieResultV2 } from "../interfaces/ISearchMovieResultV2";
|
||||||
|
@ -51,6 +51,10 @@ export class SearchV2Service extends ServiceHelpers {
|
||||||
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/Popular/${currentlyLoaded}/${toLoad}`).toPromise();
|
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/Popular/${currentlyLoaded}/${toLoad}`).toPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public advancedSearch(model: IDiscoverModel, currentlyLoaded: number, toLoad: number): Promise<ISearchMovieResult[]> {
|
||||||
|
return this.http.post<ISearchMovieResult[]>(`${this.url}/advancedSearch/Movie/${currentlyLoaded}/${toLoad}`, model).toPromise();
|
||||||
|
}
|
||||||
|
|
||||||
public upcomingMovies(): Observable<ISearchMovieResult[]> {
|
public upcomingMovies(): Observable<ISearchMovieResult[]> {
|
||||||
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/upcoming`);
|
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/upcoming`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<form [formGroup]="form" *ngIf="form">
|
<form [formGroup]="form" (ngSubmit)="onSubmit()" *ngIf="form">
|
||||||
<h1 id="advancedOptionsTitle">
|
<h1 id="advancedOptionsTitle">
|
||||||
<i class="fas fa-sliders-h"></i> Advanced Search
|
<i class="fas fa-sliders-h"></i> Advanced Search
|
||||||
</h1>
|
</h1>
|
||||||
|
@ -34,6 +34,10 @@
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<genre-select [form]="form" [mediaType]="form.controls.type.value"></genre-select>
|
<genre-select [form]="form" [mediaType]="form.controls.type.value"></genre-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<watch-providers-select [form]="form" [mediaType]="form.controls.type.value"></watch-providers-select>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -49,10 +53,10 @@
|
||||||
<button
|
<button
|
||||||
mat-raised-button
|
mat-raised-button
|
||||||
id="requestButton"
|
id="requestButton"
|
||||||
(click)="submitRequest()"
|
|
||||||
color="accent"
|
color="accent"
|
||||||
|
type="submit"
|
||||||
>
|
>
|
||||||
<i class="fas fa-plus"></i> {{ "Common.Request" | translate }}
|
<i class="fas fa-plus"></i> {{ "Common.Search" | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { Component, Inject, OnInit } from "@angular/core";
|
import { Component, Inject, OnInit } from "@angular/core";
|
||||||
import { FormBuilder, FormGroup } from "@angular/forms";
|
import { FormBuilder, FormGroup } from "@angular/forms";
|
||||||
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
|
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
|
||||||
|
import { IDiscoverModel } from "../../interfaces";
|
||||||
|
import { SearchV2Service } from "../../services";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -12,7 +14,8 @@ export class AdvancedSearchDialogComponent implements OnInit {
|
||||||
constructor(
|
constructor(
|
||||||
public dialogRef: MatDialogRef<AdvancedSearchDialogComponent, string>,
|
public dialogRef: MatDialogRef<AdvancedSearchDialogComponent, string>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||||
private fb: FormBuilder
|
private fb: FormBuilder,
|
||||||
|
private searchService: SearchV2Service,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public form: FormGroup;
|
public form: FormGroup;
|
||||||
|
@ -21,16 +24,28 @@ export class AdvancedSearchDialogComponent implements OnInit {
|
||||||
public async ngOnInit() {
|
public async ngOnInit() {
|
||||||
|
|
||||||
this.form = this.fb.group({
|
this.form = this.fb.group({
|
||||||
keywords: [[]],
|
keywordIds: [[]],
|
||||||
genres: [[]],
|
genreIds: [[]],
|
||||||
releaseYear: [],
|
releaseYear: [],
|
||||||
type: ['movie'],
|
type: ['movie'],
|
||||||
|
watchProviders: [[]],
|
||||||
})
|
})
|
||||||
|
|
||||||
this.form.controls.type.valueChanges.subscribe(val => {
|
this.form.controls.type.valueChanges.subscribe(val => {
|
||||||
this.form.controls.genres.setValue([]);
|
this.form.controls.genres.setValue([]);
|
||||||
|
this.form.controls.watchProviders.setValue([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async onSubmit() {
|
||||||
|
const watchProviderIds = <number[]>this.form.controls.watchProviders.value.map(x => x.provider_id);
|
||||||
|
const genres = <number[]>this.form.controls.genreIds.value.map(x => x.id);
|
||||||
|
await this.searchService.advancedSearch({
|
||||||
|
watchProviders: watchProviderIds,
|
||||||
|
genreIds: genres,
|
||||||
|
type: this.form.controls.type.value,
|
||||||
|
}, 0, 30);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<mat-label>Genres</mat-label>
|
<mat-label>Genres</mat-label>
|
||||||
<mat-chip-list #chipList aria-label="Fruit selection">
|
<mat-chip-list #chipList aria-label="Fruit selection">
|
||||||
<mat-chip
|
<mat-chip
|
||||||
*ngFor="let word of form.controls.genres.value"
|
*ngFor="let word of form.controls.genreIds.value"
|
||||||
[removable]="true"
|
[removable]="true"
|
||||||
(removed)="remove(word)">
|
(removed)="remove(word)">
|
||||||
{{word.name}}
|
{{word.name}}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { TheMovieDbService } from "../../../services";
|
||||||
selector: "genre-select",
|
selector: "genre-select",
|
||||||
templateUrl: "genre-select.component.html"
|
templateUrl: "genre-select.component.html"
|
||||||
})
|
})
|
||||||
export class GenreSelectComponent implements OnInit {
|
export class GenreSelectComponent {
|
||||||
constructor(
|
constructor(
|
||||||
private tmdbService: TheMovieDbService
|
private tmdbService: TheMovieDbService
|
||||||
) {}
|
) {}
|
||||||
|
@ -39,32 +39,24 @@ export class GenreSelectComponent implements OnInit {
|
||||||
|
|
||||||
@ViewChild('keywordInput') input: ElementRef<HTMLInputElement>;
|
@ViewChild('keywordInput') input: ElementRef<HTMLInputElement>;
|
||||||
|
|
||||||
async ngOnInit() {
|
|
||||||
|
|
||||||
// this.genres = await this.tmdbService.getGenres(this.mediaType).toPromise();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(word: IMovieDbKeyword): void {
|
remove(word: IMovieDbKeyword): void {
|
||||||
const exisiting = this.form.controls.genres.value;
|
const exisiting = this.form.controls.genreIds.value;
|
||||||
const index = exisiting.indexOf(word);
|
const index = exisiting.indexOf(word);
|
||||||
|
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
exisiting.splice(index, 1);
|
exisiting.splice(index, 1);
|
||||||
this.form.controls.genres.setValue(exisiting);
|
this.form.controls.genreIds.setValue(exisiting);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
selected(event: MatAutocompleteSelectedEvent): void {
|
selected(event: MatAutocompleteSelectedEvent): void {
|
||||||
const val = event.option.value;
|
const val = event.option.value;
|
||||||
const exisiting = this.form.controls.genres.value;
|
const exisiting = this.form.controls.genreIds.value;
|
||||||
if(exisiting.indexOf(val) < 0) {
|
if(exisiting.indexOf(val) < 0) {
|
||||||
exisiting.push(val);
|
exisiting.push(val);
|
||||||
}
|
}
|
||||||
this.form.controls.genres.setValue(exisiting);
|
this.form.controls.genreIds.setValue(exisiting);
|
||||||
this.input.nativeElement.value = '';
|
this.input.nativeElement.value = '';
|
||||||
this.control.setValue(null);
|
this.control.setValue(null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<mat-label>Keywords</mat-label>
|
<mat-label>Keywords</mat-label>
|
||||||
<mat-chip-list #chipList aria-label="Fruit selection">
|
<mat-chip-list #chipList aria-label="Fruit selection">
|
||||||
<mat-chip
|
<mat-chip
|
||||||
*ngFor="let word of form.controls.keywords.value"
|
*ngFor="let word of form.controls.keywordIds.value"
|
||||||
[removable]="true"
|
[removable]="true"
|
||||||
(removed)="remove(word)">
|
(removed)="remove(word)">
|
||||||
{{word.name}}
|
{{word.name}}
|
||||||
|
|
|
@ -40,23 +40,23 @@ export class KeywordSearchComponent implements OnInit {
|
||||||
};
|
};
|
||||||
|
|
||||||
remove(word: IMovieDbKeyword): void {
|
remove(word: IMovieDbKeyword): void {
|
||||||
const exisiting = this.form.controls.keywords.value;
|
const exisiting = this.form.controls.keywordIds.value;
|
||||||
const index = exisiting.indexOf(word);
|
const index = exisiting.indexOf(word);
|
||||||
|
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
exisiting.splice(index, 1);
|
exisiting.splice(index, 1);
|
||||||
this.form.controls.keywords.setValue(exisiting);
|
this.form.controls.keywordIds.setValue(exisiting);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
selected(event: MatAutocompleteSelectedEvent): void {
|
selected(event: MatAutocompleteSelectedEvent): void {
|
||||||
const val = event.option.value;
|
const val = event.option.value;
|
||||||
const exisiting = this.form.controls.keywords.value;
|
const exisiting = this.form.controls.keywordIds.value;
|
||||||
if (exisiting.indexOf(val) < 0) {
|
if (exisiting.indexOf(val) < 0) {
|
||||||
exisiting.push(val);
|
exisiting.push(val);
|
||||||
}
|
}
|
||||||
this.form.controls.keywords.setValue(exisiting);
|
this.form.controls.keywordIds.setValue(exisiting);
|
||||||
this.input.nativeElement.value = '';
|
this.input.nativeElement.value = '';
|
||||||
this.control.setValue(null);
|
this.control.setValue(null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<mat-form-field class="example-chip-list" appearance="fill">
|
||||||
|
<mat-label>Watch Providers</mat-label>
|
||||||
|
<mat-chip-list #chipList aria-label="Fruit selection">
|
||||||
|
<mat-chip
|
||||||
|
*ngFor="let word of form.controls.watchProviders.value"
|
||||||
|
[removable]="true"
|
||||||
|
(removed)="remove(word)">
|
||||||
|
{{word.provider_name}}
|
||||||
|
<mat-icon matChipRemove>cancel</mat-icon>
|
||||||
|
</mat-chip>
|
||||||
|
<input
|
||||||
|
placeholder="Search Keyword"
|
||||||
|
#keywordInput
|
||||||
|
[formControl]="control"
|
||||||
|
[matAutocomplete]="auto"
|
||||||
|
[matChipInputFor]="chipList"/>
|
||||||
|
</mat-chip-list>
|
||||||
|
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
|
||||||
|
<mat-option *ngFor="let word of filteredList | async" [value]="word">
|
||||||
|
{{word.provider_name}}
|
||||||
|
</mat-option>
|
||||||
|
</mat-autocomplete>
|
||||||
|
</mat-form-field>
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
import { Component, ElementRef, Input, OnInit, ViewChild } from "@angular/core";
|
||||||
|
import { FormControl, FormGroup } from "@angular/forms";
|
||||||
|
import { IMovieDbKeyword, IWatchProvidersResults } from "../../../interfaces";
|
||||||
|
import { debounceTime, distinctUntilChanged, map, startWith, switchMap } from "rxjs/operators";
|
||||||
|
|
||||||
|
import { MatAutocompleteSelectedEvent } from "@angular/material/autocomplete";
|
||||||
|
import { Observable } from "rxjs";
|
||||||
|
import { TheMovieDbService } from "../../../services";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "watch-providers-select",
|
||||||
|
templateUrl: "watch-providers-select.component.html"
|
||||||
|
})
|
||||||
|
export class WatchProvidersSelectComponent {
|
||||||
|
constructor(
|
||||||
|
private tmdbService: TheMovieDbService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private _mediaType: string;
|
||||||
|
@Input() set mediaType(type: string) {
|
||||||
|
this._mediaType = type;
|
||||||
|
this.tmdbService.getWatchProviders(this._mediaType).subscribe((res) => {
|
||||||
|
this.watchProviders = res;
|
||||||
|
this.filteredList = this.control.valueChanges.pipe(
|
||||||
|
startWith(''),
|
||||||
|
map((genre: string | null) => genre ? this._filter(genre) : this.watchProviders.slice()));
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
get mediaType(): string {
|
||||||
|
return this._mediaType;
|
||||||
|
}
|
||||||
|
@Input() public form: FormGroup;
|
||||||
|
|
||||||
|
public watchProviders: IWatchProvidersResults[] = [];
|
||||||
|
public control = new FormControl();
|
||||||
|
public filteredTags: IWatchProvidersResults[];
|
||||||
|
public filteredList: Observable<IWatchProvidersResults[]>;
|
||||||
|
|
||||||
|
@ViewChild('keywordInput') input: ElementRef<HTMLInputElement>;
|
||||||
|
|
||||||
|
|
||||||
|
remove(word: IWatchProvidersResults): void {
|
||||||
|
const exisiting = this.form.controls.watchProviders.value;
|
||||||
|
const index = exisiting.indexOf(word);
|
||||||
|
|
||||||
|
if (index >= 0) {
|
||||||
|
exisiting.splice(index, 1);
|
||||||
|
this.form.controls.watchProviders.setValue(exisiting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
selected(event: MatAutocompleteSelectedEvent): void {
|
||||||
|
const val = event.option.value;
|
||||||
|
const exisiting = this.form.controls.watchProviders.value;
|
||||||
|
if (exisiting.indexOf(val) < 0) {
|
||||||
|
exisiting.push(val);
|
||||||
|
}
|
||||||
|
this.form.controls.watchProviders.setValue(exisiting);
|
||||||
|
this.input.nativeElement.value = '';
|
||||||
|
this.control.setValue(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _filter(value: string|IWatchProvidersResults): IWatchProvidersResults[] {
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
const filterValue = value.provider_name.toLowerCase();
|
||||||
|
return this.watchProviders.filter(g => g.provider_name.toLowerCase().includes(filterValue));
|
||||||
|
} else if (typeof value === 'string') {
|
||||||
|
const filterValue = value.toLowerCase();
|
||||||
|
return this.watchProviders.filter(g => g.provider_name.toLowerCase().includes(filterValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.watchProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -41,6 +41,7 @@ import { SidebarModule } from "primeng/sidebar";
|
||||||
import { TheMovieDbService } from "../services";
|
import { TheMovieDbService } from "../services";
|
||||||
import { TranslateModule } from "@ngx-translate/core";
|
import { TranslateModule } from "@ngx-translate/core";
|
||||||
import { TruncateModule } from "@yellowspot/ng-truncate";
|
import { TruncateModule } from "@yellowspot/ng-truncate";
|
||||||
|
import { WatchProvidersSelectComponent } from "./components/watch-providers-select/watch-providers-select.component";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -51,6 +52,7 @@ import { TruncateModule } from "@yellowspot/ng-truncate";
|
||||||
AdvancedSearchDialogComponent,
|
AdvancedSearchDialogComponent,
|
||||||
KeywordSearchComponent,
|
KeywordSearchComponent,
|
||||||
GenreSelectComponent,
|
GenreSelectComponent,
|
||||||
|
WatchProvidersSelectComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
SidebarModule,
|
SidebarModule,
|
||||||
|
@ -99,6 +101,7 @@ import { TruncateModule } from "@yellowspot/ng-truncate";
|
||||||
AdvancedSearchDialogComponent,
|
AdvancedSearchDialogComponent,
|
||||||
GenreSelectComponent,
|
GenreSelectComponent,
|
||||||
KeywordSearchComponent,
|
KeywordSearchComponent,
|
||||||
|
WatchProvidersSelectComponent,
|
||||||
DetailsGroupComponent,
|
DetailsGroupComponent,
|
||||||
TruncateModule,
|
TruncateModule,
|
||||||
InputSwitchModule,
|
InputSwitchModule,
|
||||||
|
|
|
@ -46,5 +46,23 @@ namespace Ombi.Controllers.External
|
||||||
[HttpGet("Genres/{media}")]
|
[HttpGet("Genres/{media}")]
|
||||||
public async Task<IEnumerable<Genre>> GetGenres(string media) =>
|
public async Task<IEnumerable<Genre>> GetGenres(string media) =>
|
||||||
await TmdbApi.GetGenres(media, HttpContext.RequestAborted);
|
await TmdbApi.GetGenres(media, HttpContext.RequestAborted);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Searches for the watch providers matching the specified term.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="searchTerm">The search term.</param>
|
||||||
|
[HttpGet("WatchProviders/movie")]
|
||||||
|
public async Task<IEnumerable<WatchProvidersResults>> GetWatchProvidersMovies([FromQuery] string searchTerm) =>
|
||||||
|
await TmdbApi.SearchWatchProviders("movie", searchTerm, HttpContext.RequestAborted);
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Searches for the watch providers matching the specified term.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="searchTerm">The search term.</param>
|
||||||
|
[HttpGet("WatchProviders/tv")]
|
||||||
|
public async Task<IEnumerable<WatchProvidersResults>> GetWatchProvidersTv([FromQuery] string searchTerm) =>
|
||||||
|
await TmdbApi.SearchWatchProviders("tv", searchTerm, HttpContext.RequestAborted);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,6 +183,19 @@ namespace Ombi.Controllers.V2
|
||||||
DateTimeOffset.Now.AddHours(12));
|
DateTimeOffset.Now.AddHours(12));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns Advanced Searched Media using paging
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>We use TheMovieDb as the Movie Provider</remarks>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost("advancedSearch/movie/{currentPosition}/{amountToLoad}")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesDefaultResponseType]
|
||||||
|
public Task<IEnumerable<SearchMovieViewModel>> AdvancedSearchMovie([FromBody]DiscoverModel model, int currentPosition, int amountToLoad)
|
||||||
|
{
|
||||||
|
return _movieEngineV2.AdvancedSearch(model, currentPosition, amountToLoad, Request.HttpContext.RequestAborted);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns Seasonal Movies
|
/// Returns Seasonal Movies
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue