mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-19 21:03:17 -07:00
!minor Poc'ing a new search page
This commit is contained in:
parent
f8054317ab
commit
989be6df6c
4 changed files with 322 additions and 0 deletions
|
@ -27,3 +27,7 @@
|
||||||
requestProcessing: boolean;
|
requestProcessing: boolean;
|
||||||
processed: boolean;
|
processed: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ISearchMovieResultContainer {
|
||||||
|
movies: ISearchMovieResult[];
|
||||||
|
}
|
||||||
|
|
149
src/Ombi/ClientApp/app/search/moviesearchgrid.component.html
Normal file
149
src/Ombi/ClientApp/app/search/moviesearchgrid.component.html
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
<!-- Movie tab -->
|
||||||
|
<div role="tabpanel" class="tab-pane active" id="MoviesTab">
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="search" type="text" class="form-control form-control-custom form-control-search form-control-withbuttons" (keyup)="search($event)">
|
||||||
|
<div class="input-group-addon right-radius">
|
||||||
|
<div class="btn-group">
|
||||||
|
<a href="#" class="btn btn-sm btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||||
|
Suggestions
|
||||||
|
<i class="fa fa-chevron-down"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li>
|
||||||
|
<a (click)="popularMovies()">Popular Movies</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a (click)="upcomingMovies()">Upcoming Movies</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a (click)="topRatedMovies()">Top Rated Movies</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a (click)="nowPlayingMovies()">Now Playing Movies</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<i id="movieSearchButton" class="fa fa-search"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<!-- Movie content -->
|
||||||
|
<div id="movieList">
|
||||||
|
<div *ngIf="searchApplied && movieResults?.length <= 0" class='no-search-results'>
|
||||||
|
<i class='fa fa-film no-search-results-icon'></i>
|
||||||
|
<div class='no-search-results-text'>Sorry, we didn't find any results!</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--NEW-->
|
||||||
|
<div *ngFor="let grid of movieResultGrid; let i = index">
|
||||||
|
<div class="row">
|
||||||
|
<div *ngFor="let r of grid.movies">
|
||||||
|
<div class="col-md-3">
|
||||||
|
|
||||||
|
<img *ngIf="r.posterPath" class="img-responsive poster" src="https://image.tmdb.org/t/p/w150/{{r.posterPath}}"
|
||||||
|
alt="poster">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--END NEW-->
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
<div *ngFor="let result of movieResults">
|
||||||
|
<div class="row">
|
||||||
|
<div id="imgDiv" class="col-sm-2">
|
||||||
|
|
||||||
|
|
||||||
|
<img *ngIf="result.posterPath" class="img-responsive poster" src="https://image.tmdb.org/t/p/w150/{{result.posterPath}}"
|
||||||
|
alt="poster">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<div>
|
||||||
|
<a href="https://www.themoviedb.org/movie/{{result.id}}/" target="_blank">
|
||||||
|
<h4>{{result.title}} ({{result.releaseDate | date: 'yyyy'}})</h4>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<span *ngIf="result.releaseDate" class="label label-info" target="_blank">Release Date: {{result.releaseDate | date: 'dd/MM/yyyy'}}</span>
|
||||||
|
|
||||||
|
<a *ngIf="result.homepage" href="{{result.homepage}}" target="_blank">
|
||||||
|
<span class="label label-info">HomePage</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a *ngIf="result.trailer" href="{{result.trailer}}" target="_blank">
|
||||||
|
<span class="label label-info">Trailer</span>
|
||||||
|
</a>
|
||||||
|
<span *ngIf="result.quality" class="label label-success">{{result.quality}}p</span>
|
||||||
|
|
||||||
|
<ng-template [ngIf]="result.available">
|
||||||
|
<span class="label label-success">Available</span>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template [ngIf]="result.approved && !result.available">
|
||||||
|
<span class="label label-info">Processing Request</span>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template [ngIf]="result.requested && !result.approved && !result.available">
|
||||||
|
<span class="label label-warning">Pending Approval</span>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template [ngIf]="!result.requested && !result.available && !result.approved">
|
||||||
|
<span class="label label-danger">Not Requested</span>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<p style="font-size: 0.9rem !important">{{result.overview}}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="col-sm-2">
|
||||||
|
|
||||||
|
<div *ngIf="result.available">
|
||||||
|
<button style="text-align: right" class="btn btn-success-outline disabled" disabled>
|
||||||
|
<i class="fa fa-check"></i> Available</button>
|
||||||
|
|
||||||
|
<div *ngIf="result.plexUrl">
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<a style="text-align: right" class="btn btn-sm btn-primary-outline" href="{{result.plexUrl}}" target="_blank">
|
||||||
|
<i class="fa fa-eye"></i> View In Plex</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="!result.available">
|
||||||
|
<div *ngIf="result.requested || result.approved; then requestedBtn else notRequestedBtn"></div>
|
||||||
|
<ng-template #requestedBtn>
|
||||||
|
<button style="text-align: right" class="btn btn-primary-outline disabled" [disabled]>
|
||||||
|
<i class="fa fa-check"></i> Requested</button>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template #notRequestedBtn>
|
||||||
|
<button id="{{result.id}}" style="text-align: right" class="btn btn-primary-outline" (click)="request(result)">
|
||||||
|
<i *ngIf="result.requestProcessing" class="fa fa-circle-o-notch fa-spin fa-fw"></i>
|
||||||
|
<i *ngIf="!result.requestProcessing && !result.processed" class="fa fa-plus"></i>
|
||||||
|
<i *ngIf="result.processed && !result.requestProcessing" class="fa fa-check"></i>Request</button>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<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> View On Plex</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
167
src/Ombi/ClientApp/app/search/moviesearchgrid.component.ts
Normal file
167
src/Ombi/ClientApp/app/search/moviesearchgrid.component.ts
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
import "rxjs/add/operator/debounceTime";
|
||||||
|
import "rxjs/add/operator/distinctUntilChanged";
|
||||||
|
import "rxjs/add/operator/map";
|
||||||
|
import { Subject } from "rxjs/Subject";
|
||||||
|
|
||||||
|
import { AuthService } from "../auth/auth.service";
|
||||||
|
import { NotificationService, RequestService, SearchService } from "../services";
|
||||||
|
|
||||||
|
import { IRequestEngineResult, ISearchMovieResult, ISearchMovieResultContainer } from "../interfaces";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "movie-search-grid",
|
||||||
|
templateUrl: "./moviesearchgrid.component.html",
|
||||||
|
})
|
||||||
|
export class MovieSearchGridComponent implements OnInit {
|
||||||
|
|
||||||
|
public searchText: string;
|
||||||
|
public searchChanged: Subject<string> = new Subject<string>();
|
||||||
|
public movieResults: ISearchMovieResult[];
|
||||||
|
public movieResultGrid: ISearchMovieResultContainer[] = [];
|
||||||
|
public result: IRequestEngineResult;
|
||||||
|
public searchApplied = false;
|
||||||
|
|
||||||
|
constructor(private searchService: SearchService, private requestService: RequestService,
|
||||||
|
private notificationService: NotificationService, private authService: AuthService) {
|
||||||
|
|
||||||
|
this.searchChanged
|
||||||
|
.debounceTime(600) // Wait Xms afterthe last event before emitting last event
|
||||||
|
.distinctUntilChanged() // only emit if value is different from previous value
|
||||||
|
.subscribe(x => {
|
||||||
|
this.searchText = x as string;
|
||||||
|
if (this.searchText === "") {
|
||||||
|
this.clearResults();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.searchService.searchMovie(this.searchText)
|
||||||
|
.subscribe(x => {
|
||||||
|
this.movieResults = x;
|
||||||
|
this.searchApplied = true;
|
||||||
|
// Now let's load some exta info including IMDBId
|
||||||
|
// This way the search is fast at displaying results.
|
||||||
|
this.getExtaInfo();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnInit() {
|
||||||
|
this.searchText = "";
|
||||||
|
this.movieResults = [];
|
||||||
|
this.result = {
|
||||||
|
message: "",
|
||||||
|
requestAdded: false,
|
||||||
|
errorMessage: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public search(text: any) {
|
||||||
|
this.searchChanged.next(text.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public request(searchResult: ISearchMovieResult) {
|
||||||
|
searchResult.requested = true;
|
||||||
|
searchResult.requestProcessing = true;
|
||||||
|
if (this.authService.hasRole("admin") || this.authService.hasRole("AutoApproveMovie")) {
|
||||||
|
searchResult.approved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.requestService.requestMovie(searchResult)
|
||||||
|
.subscribe(x => {
|
||||||
|
this.result = x;
|
||||||
|
|
||||||
|
if (this.result.requestAdded) {
|
||||||
|
this.notificationService.success("Request Added",
|
||||||
|
`Request for ${searchResult.title} has been added successfully`);
|
||||||
|
searchResult.processed = true;
|
||||||
|
} else {
|
||||||
|
if (this.result.errorMessage && this.result.message) {
|
||||||
|
this.notificationService.warning("Request Added", `${this.result.message} - ${this.result.errorMessage}`);
|
||||||
|
} else {
|
||||||
|
this.notificationService.warning("Request Added", this.result.message ? this.result.message : this.result.errorMessage);
|
||||||
|
}
|
||||||
|
searchResult.requested = false;
|
||||||
|
searchResult.approved = false;
|
||||||
|
searchResult.processed = false;
|
||||||
|
searchResult.requestProcessing = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
|
searchResult.processed = false;
|
||||||
|
searchResult.requestProcessing = false;
|
||||||
|
this.notificationService.error("Something went wrong", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public popularMovies() {
|
||||||
|
this.clearResults();
|
||||||
|
this.searchService.popularMovies()
|
||||||
|
.subscribe(x => {
|
||||||
|
this.movieResults = x;
|
||||||
|
this.processGrid(x);
|
||||||
|
this.getExtaInfo();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public nowPlayingMovies() {
|
||||||
|
this.clearResults();
|
||||||
|
this.searchService.nowPlayingMovies()
|
||||||
|
.subscribe(x => {
|
||||||
|
this.movieResults = x;
|
||||||
|
this.getExtaInfo();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public topRatedMovies() {
|
||||||
|
this.clearResults();
|
||||||
|
this.searchService.topRatedMovies()
|
||||||
|
.subscribe(x => {
|
||||||
|
this.movieResults = x;
|
||||||
|
this.getExtaInfo();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public upcomingMovies() {
|
||||||
|
this.clearResults();
|
||||||
|
this.searchService.upcomignMovies()
|
||||||
|
.subscribe(x => {
|
||||||
|
this.movieResults = x;
|
||||||
|
this.getExtaInfo();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getExtaInfo() {
|
||||||
|
this.movieResults.forEach((val, index) => {
|
||||||
|
this.searchService.getMovieInformation(val.id)
|
||||||
|
.subscribe(m => this.updateItem(val, m));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateItem(key: ISearchMovieResult, updated: ISearchMovieResult) {
|
||||||
|
const index = this.movieResults.indexOf(key, 0);
|
||||||
|
if (index > -1) {
|
||||||
|
this.movieResults[index] = updated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private clearResults() {
|
||||||
|
this.movieResults = [];
|
||||||
|
this.searchApplied = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private processGrid(movies: ISearchMovieResult[]) {
|
||||||
|
let container = <ISearchMovieResultContainer>{ movies: [] };
|
||||||
|
movies.forEach((movie, i) => {
|
||||||
|
i++;
|
||||||
|
if((i % 4) === 0) {
|
||||||
|
container.movies.push(movie);
|
||||||
|
this.movieResultGrid.push(container);
|
||||||
|
container = <ISearchMovieResultContainer>{ movies: [] };
|
||||||
|
} else {
|
||||||
|
|
||||||
|
container.movies.push(movie);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.movieResultGrid.push(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import { RouterModule, Routes } from "@angular/router";
|
||||||
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
|
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
|
||||||
|
|
||||||
import { MovieSearchComponent } from "./moviesearch.component";
|
import { MovieSearchComponent } from "./moviesearch.component";
|
||||||
|
import { MovieSearchGridComponent } from "./moviesearchgrid.component";
|
||||||
import { SearchComponent } from "./search.component";
|
import { SearchComponent } from "./search.component";
|
||||||
import { SeriesInformationComponent } from "./seriesinformation.component";
|
import { SeriesInformationComponent } from "./seriesinformation.component";
|
||||||
import { TvSearchComponent } from "./tvsearch.component";
|
import { TvSearchComponent } from "./tvsearch.component";
|
||||||
|
@ -35,6 +36,7 @@ const routes: Routes = [
|
||||||
MovieSearchComponent,
|
MovieSearchComponent,
|
||||||
TvSearchComponent,
|
TvSearchComponent,
|
||||||
SeriesInformationComponent,
|
SeriesInformationComponent,
|
||||||
|
MovieSearchGridComponent,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
RouterModule,
|
RouterModule,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue