Added the ability to specify a year when searching for movies

pull/2728/head
tidusjar 6 years ago
parent 8154334dae
commit 53cdcdc23b

@ -10,7 +10,7 @@ namespace Ombi.Core
Task<IEnumerable<SearchMovieViewModel>> PopularMovies(); Task<IEnumerable<SearchMovieViewModel>> PopularMovies();
Task<IEnumerable<SearchMovieViewModel>> Search(string search); Task<IEnumerable<SearchMovieViewModel>> Search(string search, int? year);
Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies(); Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies();

@ -36,6 +36,8 @@ namespace Ombi.Core.Engine
private IMapper Mapper { get; } private IMapper Mapper { get; }
private ILogger<MovieSearchEngine> Logger { get; } private ILogger<MovieSearchEngine> Logger { get; }
private const int MovieLimit = 10;
/// <summary> /// <summary>
/// Lookups the imdb information. /// Lookups the imdb information.
/// </summary> /// </summary>
@ -54,13 +56,13 @@ namespace Ombi.Core.Engine
/// </summary> /// </summary>
/// <param name="search">The search.</param> /// <param name="search">The search.</param>
/// <returns></returns> /// <returns></returns>
public async Task<IEnumerable<SearchMovieViewModel>> Search(string search) public async Task<IEnumerable<SearchMovieViewModel>> Search(string search, int? year)
{ {
var result = await MovieApi.SearchMovie(search); var result = await MovieApi.SearchMovie(search, year);
if (result != null) if (result != null)
{ {
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
} }
return null; return null;
} }
@ -76,7 +78,7 @@ namespace Ombi.Core.Engine
if (result != null) if (result != null)
{ {
Logger.LogDebug("Search Result: {result}", result); Logger.LogDebug("Search Result: {result}", result);
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
} }
return null; return null;
} }
@ -90,7 +92,7 @@ namespace Ombi.Core.Engine
var result = await Cache.GetOrAdd(CacheKeys.PopularMovies, async () => await MovieApi.PopularMovies(), DateTime.Now.AddHours(12)); var result = await Cache.GetOrAdd(CacheKeys.PopularMovies, async () => await MovieApi.PopularMovies(), DateTime.Now.AddHours(12));
if (result != null) if (result != null)
{ {
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
} }
return null; return null;
} }
@ -104,7 +106,7 @@ namespace Ombi.Core.Engine
var result = await Cache.GetOrAdd(CacheKeys.TopRatedMovies, async () => await MovieApi.TopRated(), DateTime.Now.AddHours(12)); var result = await Cache.GetOrAdd(CacheKeys.TopRatedMovies, async () => await MovieApi.TopRated(), DateTime.Now.AddHours(12));
if (result != null) if (result != null)
{ {
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
} }
return null; return null;
} }
@ -119,7 +121,7 @@ namespace Ombi.Core.Engine
if (result != null) if (result != null)
{ {
Logger.LogDebug("Search Result: {result}", result); Logger.LogDebug("Search Result: {result}", result);
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
} }
return null; return null;
} }
@ -133,7 +135,7 @@ namespace Ombi.Core.Engine
var result = await Cache.GetOrAdd(CacheKeys.NowPlayingMovies, async () => await MovieApi.NowPlaying(), DateTime.Now.AddHours(12)); var result = await Cache.GetOrAdd(CacheKeys.NowPlayingMovies, async () => await MovieApi.NowPlaying(), DateTime.Now.AddHours(12));
if (result != null) if (result != null)
{ {
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API return await TransformMovieResultsToResponse(result.Take(MovieLimit)); // Take x to stop us overloading the API
} }
return null; return null;
} }

@ -11,7 +11,7 @@ namespace Ombi.Api.TheMovieDb
Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId); Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId);
Task<List<MovieSearchResult>> NowPlaying(); Task<List<MovieSearchResult>> NowPlaying();
Task<List<MovieSearchResult>> PopularMovies(); Task<List<MovieSearchResult>> PopularMovies();
Task<List<MovieSearchResult>> SearchMovie(string searchTerm); Task<List<MovieSearchResult>> SearchMovie(string searchTerm, int? year);
Task<List<TvSearchResult>> SearchTv(string searchTerm); Task<List<TvSearchResult>> SearchTv(string searchTerm);
Task<List<MovieSearchResult>> TopRated(); Task<List<MovieSearchResult>> TopRated();
Task<List<MovieSearchResult>> Upcoming(); Task<List<MovieSearchResult>> Upcoming();

@ -83,11 +83,15 @@ namespace Ombi.Api.TheMovieDb
return Mapper.Map<MovieResponseDto>(result); return Mapper.Map<MovieResponseDto>(result);
} }
public async Task<List<MovieSearchResult>> SearchMovie(string searchTerm) public async Task<List<MovieSearchResult>> SearchMovie(string searchTerm, int? year)
{ {
var request = new Request($"search/movie", BaseUri, HttpMethod.Get); var request = new Request($"search/movie", BaseUri, HttpMethod.Get);
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm); request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm);
if(year.HasValue && year.Value > 0)
{
request.FullUri = request.FullUri.AddQueryParameter("year", year.Value.ToString());
}
AddRetry(request); AddRetry(request);
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request); var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);

@ -39,7 +39,7 @@ export class MusicRequestsComponent implements OnInit {
public rejectionReason: string; public rejectionReason: string;
public totalAlbums: number = 100; public totalAlbums: number = 100;
private currentlyLoaded: number; public currentlyLoaded: number;
private amountToLoad: number; private amountToLoad: number;
constructor( constructor(

@ -5,7 +5,7 @@
<input id="search" type="text" class="form-control form-control-custom form-control-search form-control-withbuttons" <input id="search" type="text" class="form-control form-control-custom form-control-search form-control-withbuttons"
(keyup)="search($event)"> (keyup)="search($event)">
<div class="input-group-addon right-radius"> <div class="input-group-addon right-radius">
<div class="btn-group"> <div class="btn-group" role="group">
<a href="#" class="btn btn-sm btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false"> <a href="#" class="btn btn-sm btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
{{ 'Search.Suggestions' | translate }} {{ 'Search.Suggestions' | translate }}
<i class="fa fa-chevron-down"></i> <i class="fa fa-chevron-down"></i>
@ -16,10 +16,25 @@
<li><a (click)="topRatedMovies()" [translate]="'Search.Movies.TopRatedMovies'"></a></li> <li><a (click)="topRatedMovies()" [translate]="'Search.Movies.TopRatedMovies'"></a></li>
<li><a (click)="nowPlayingMovies()" [translate]="'Search.Movies.NowPlayingMovies'"></a></li> <li><a (click)="nowPlayingMovies()" [translate]="'Search.Movies.NowPlayingMovies'"></a></li>
</ul> </ul>
<button class="btn btn-sm btn-primary-outline" (click)="refineOpen()">
{{ 'Search.Refine' | translate }}
<i class="fa" [ngClass]="{'fa-chevron-down': !refineSearchEnabled, 'fa-chevron-up': refineSearchEnabled}"></i>
</button>
</div> </div>
<i class="fa fa-search"></i> <i class="fa fa-search"></i>
</div> </div>
</div> </div>
<!-- Refine search options -->
<div class="row top-spacing" *ngIf="refineSearchEnabled">
<div class="col-md-2">
<input [(ngModel)]="searchYear" class="form-control form-control-custom" placeholder="Year">
</div>
<div class="col-md-10">
<button class="btn pull-right btn-success-outline" (click)="applyRefinedSearch()">Apply</button>
</div>
</div>
<remaining-requests [movie]="true" [quotaRefreshEvents]="movieRequested.asObservable()" #remainingFilms></remaining-requests> <remaining-requests [movie]="true" [quotaRefreshEvents]="movieRequested.asObservable()" #remainingFilms></remaining-requests>
@ -111,11 +126,15 @@
<br /> <br />
<div *ngIf="result.available"> <div *ngIf="result.available">
<a *ngIf="result.plexUrl" style="text-align: right" class="btn btn-sm btn-success-outline" href="{{result.plexUrl}}" target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnPlex' | translate}}</a> <a *ngIf="result.plexUrl" style="text-align: right" class="btn btn-sm btn-success-outline" href="{{result.plexUrl}}"
<a *ngIf="result.embyUrl" style="text-align: right" id="embybtn" class="btn btn-sm btn-success-outline" href="{{result.embyUrl}}" target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnEmby' | translate}}</a> target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnPlex' | translate}}</a>
<a *ngIf="result.embyUrl" style="text-align: right" id="embybtn" class="btn btn-sm btn-success-outline"
href="{{result.embyUrl}}" target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnEmby' |
translate}}</a>
</div> </div>
<div class="dropdown" *ngIf="result.available && issueCategories && issuesEnabled"> <div class="dropdown" *ngIf="result.available && issueCategories && issuesEnabled">
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> <button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="true">
<i class="fa fa-plus"></i> {{'Requests.ReportIssue' | translate}} <i class="fa fa-plus"></i> {{'Requests.ReportIssue' | translate}}
<span class="caret"></span> <span class="caret"></span>
</button> </button>

@ -12,6 +12,7 @@ import { NotificationService, RequestService, SearchService } from "../services"
@Component({ @Component({
selector: "movie-search", selector: "movie-search",
templateUrl: "./moviesearch.component.html", templateUrl: "./moviesearch.component.html",
styleUrls: ["./search.component.scss"],
}) })
export class MovieSearchComponent implements OnInit { export class MovieSearchComponent implements OnInit {
@ -22,6 +23,8 @@ export class MovieSearchComponent implements OnInit {
public result: IRequestEngineResult; public result: IRequestEngineResult;
public searchApplied = false; public searchApplied = false;
public refineSearchEnabled = false;
public searchYear?: number;
@Input() public issueCategories: IIssueCategory[]; @Input() public issueCategories: IIssueCategory[];
@Input() public issuesEnabled: boolean; @Input() public issuesEnabled: boolean;
@ -46,14 +49,7 @@ export class MovieSearchComponent implements OnInit {
this.clearResults(); this.clearResults();
return; return;
} }
this.searchService.searchMovie(this.searchText) this.runSearch();
.subscribe(x => {
this.movieResults = x;
this.searchApplied = true;
// Now let's load some extra info including IMDB Id
// This way the search is fast at displaying results.
this.getExtraInfo();
});
}); });
this.defaultPoster = "../../../images/default_movie_poster.png"; this.defaultPoster = "../../../images/default_movie_poster.png";
const base = this.platformLocation.getBaseHrefFromDOM(); const base = this.platformLocation.getBaseHrefFromDOM();
@ -184,6 +180,17 @@ export class MovieSearchComponent implements OnInit {
}); });
} }
public refineOpen() {
this.refineSearchEnabled = !this.refineSearchEnabled;
if(!this.refineSearchEnabled) {
this.searchYear = undefined;
}
}
public applyRefinedSearch() {
this.runSearch();
}
private getExtraInfo() { private getExtraInfo() {
this.movieResults.forEach((val, index) => { this.movieResults.forEach((val, index) => {
@ -214,4 +221,15 @@ export class MovieSearchComponent implements OnInit {
this.movieResults = []; this.movieResults = [];
this.searchApplied = false; this.searchApplied = false;
} }
private runSearch() {
this.searchService.searchMovie(this.searchText, this.searchYear)
.subscribe(x => {
this.movieResults = x;
this.searchApplied = true;
// Now let's load some extra info including IMDB Id
// This way the search is fast at displaying results.
this.getExtraInfo();
});
}
} }

@ -0,0 +1,10 @@
@media (max-width: 978px) {
.top-spacing {
padding-top: 5%
}
}
@media (min-width: 979px) {
.top-spacing {
padding-top: 2%
}
}

@ -17,8 +17,12 @@ export class SearchService extends ServiceHelpers {
} }
// Movies // Movies
public searchMovie(searchTerm: string): Observable<ISearchMovieResult[]> { public searchMovie(searchTerm: string, year?: number): Observable<ISearchMovieResult[]> {
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/` + searchTerm); if(year && year > 0) {
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/${searchTerm}/${year}`);
} else {
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/${searchTerm}`);
}
} }
public similarMovies(theMovieDbId: number): Observable<ISearchMovieResult[]> { public similarMovies(theMovieDbId: number): Observable<ISearchMovieResult[]> {
return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/${theMovieDbId}/similar`); return this.http.get<ISearchMovieResult[]>(`${this.url}/Movie/${theMovieDbId}/similar`);

@ -46,7 +46,25 @@ namespace Ombi.Controllers
{ {
Logger.LogDebug("Searching : {searchTerm}", searchTerm); Logger.LogDebug("Searching : {searchTerm}", searchTerm);
return await MovieEngine.Search(searchTerm); return await MovieEngine.Search(searchTerm, null);
}
}
/// <summary>
/// Searches for a movie.
/// </summary>
/// <remarks>We use TheMovieDb as the Movie Provider</remarks>
/// <param name="searchTerm">The search term.</param>
/// <param name="year">optional year parameter</param>
/// <returns></returns>
[HttpGet("movie/{searchTerm}/{year}")]
public async Task<IEnumerable<SearchMovieViewModel>> SearchMovie(string searchTerm, int? year)
{
using (MiniProfiler.Current.Step("SearchingMovie"))
{
Logger.LogDebug("Searching : {searchTerm}, Year: {year}", searchTerm, year);
return await MovieEngine.Search(searchTerm, year);
} }
} }

@ -8,7 +8,7 @@
<FileVersion>$(SemVer)</FileVersion> <FileVersion>$(SemVer)</FileVersion>
<Version>$(FullVer)</Version> <Version>$(FullVer)</Version>
<PackageVersion></PackageVersion> <PackageVersion></PackageVersion>
<TypeScriptToolsVersion>3.0</TypeScriptToolsVersion> <TypeScriptToolsVersion>3.1</TypeScriptToolsVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<ServerGarbageCollection>false</ServerGarbageCollection> <ServerGarbageCollection>false</ServerGarbageCollection>

@ -60,6 +60,7 @@
"jquery": "^3.3.1", "jquery": "^3.3.1",
"mini-css-extract-plugin": "^0.4.1", "mini-css-extract-plugin": "^0.4.1",
"moment": "^2.22.2", "moment": "^2.22.2",
"natives": "1.1.6",
"ng2-cookies": "^1.0.12", "ng2-cookies": "^1.0.12",
"ngx-clipboard": "^11.1.1", "ngx-clipboard": "^11.1.1",
"ngx-infinite-scroll": "^6.0.1", "ngx-infinite-scroll": "^6.0.1",

@ -78,6 +78,7 @@
"ViewOnEmby": "View On Emby", "ViewOnEmby": "View On Emby",
"RequestAdded": "Request for {{title}} has been added successfully", "RequestAdded": "Request for {{title}} has been added successfully",
"Similar":"Similar", "Similar":"Similar",
"Refine":"Refine",
"Movies": { "Movies": {
"PopularMovies": "Popular Movies", "PopularMovies": "Popular Movies",
"UpcomingMovies": "Upcoming Movies", "UpcomingMovies": "Upcoming Movies",

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save