mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-06 05:01:13 -07:00
feat(discover): ✨ Added infinite scroll on advanced search results
* feat(discover): ✨ Added infinite scroll on advanced search results
This commit is contained in:
parent
e00e39a1be
commit
898bc89fa7
12 changed files with 88 additions and 40 deletions
|
@ -152,15 +152,15 @@ namespace Ombi.Core.Engine.V2
|
|||
{
|
||||
var langCode = await DefaultLanguageCode(null);
|
||||
|
||||
//var pages = PaginationHelper.GetNextPages(currentlyLoaded, toLoad, _theMovieDbMaxPageItems);
|
||||
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));
|
||||
//}
|
||||
return await TransformMovieResultsToResponse(apiResult);
|
||||
foreach (var pagesToLoad in pages)
|
||||
{
|
||||
var apiResult = await MovieApi.AdvancedSearch(model, pagesToLoad.Page, cancellationToken);
|
||||
results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take));
|
||||
}
|
||||
return await TransformMovieResultsToResponse(results);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -45,6 +45,6 @@ namespace Ombi.Api.TheMovieDb
|
|||
Task<List<Genre>> GetGenres(string media, CancellationToken cancellationToken, string languageCode);
|
||||
Task<List<Language>> GetLanguages(CancellationToken cancellationToken);
|
||||
Task<List<WatchProvidersResults>> SearchWatchProviders(string media, string searchTerm, CancellationToken cancellationToken);
|
||||
Task<List<MovieDbSearchResult>> AdvancedSearch(DiscoverModel model, CancellationToken cancellationToken);
|
||||
Task<List<MovieDbSearchResult>> AdvancedSearch(DiscoverModel model, int page, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,11 +70,11 @@ namespace Ombi.Api.TheMovieDb
|
|||
|
||||
|
||||
|
||||
public async Task<List<MovieDbSearchResult>> AdvancedSearch(DiscoverModel model, CancellationToken cancellationToken)
|
||||
public async Task<List<MovieDbSearchResult>> AdvancedSearch(DiscoverModel model, int page, 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)
|
||||
if (model.ReleaseYear.HasValue && model.ReleaseYear.Value > 1900)
|
||||
{
|
||||
request.FullUri = request.FullUri.AddQueryParameter("year", model.ReleaseYear.Value.ToString());
|
||||
}
|
||||
|
@ -92,6 +92,9 @@ namespace Ombi.Api.TheMovieDb
|
|||
}
|
||||
//request.FullUri = request.FullUri.AddQueryParameter("sort_by", "popularity.desc");
|
||||
|
||||
request.AddQueryString("page", page.ToString());
|
||||
|
||||
|
||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request, cancellationToken);
|
||||
return Mapper.Map<List<MovieDbSearchResult>>(result.results);
|
||||
}
|
||||
|
@ -139,7 +142,7 @@ namespace Ombi.Api.TheMovieDb
|
|||
var result = await Api.Request<ActorCredits>(request);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public async Task<ActorCredits> GetActorTvCredits(int actorId, string langCode)
|
||||
{
|
||||
var request = new Request($"person/{actorId}/tv_credits", BaseUri, HttpMethod.Get);
|
||||
|
@ -281,7 +284,7 @@ namespace Ombi.Api.TheMovieDb
|
|||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||
return Mapper.Map<List<MovieDbSearchResult>>(result.results);
|
||||
}
|
||||
|
||||
|
||||
public Task<List<MovieDbSearchResult>> TrendingMovies(string langCode, int? page = null)
|
||||
{
|
||||
return Trending("movie", langCode, page);
|
||||
|
@ -295,7 +298,7 @@ namespace Ombi.Api.TheMovieDb
|
|||
{
|
||||
// https://developers.themoviedb.org/3/trending/get-trending
|
||||
var timeWindow = "week"; // another option can be 'day'
|
||||
var request = new Request($"trending/{type}/{timeWindow}", BaseUri, HttpMethod.Get);
|
||||
var request = new Request($"trending/{type}/{timeWindow}", BaseUri, HttpMethod.Get);
|
||||
request.AddQueryString("api_key", ApiToken);
|
||||
request.AddQueryString("language", langCode);
|
||||
|
||||
|
@ -413,8 +416,8 @@ namespace Ombi.Api.TheMovieDb
|
|||
request.AddQueryString("language", langCode);
|
||||
request.AddQueryString("sort_by", "vote_average.desc");
|
||||
|
||||
request.AddQueryString("with_keywords", keywordId);
|
||||
|
||||
request.AddQueryString("with_keywords", keywordId);
|
||||
|
||||
// `vote_count` consideration isn't explicitly documented, but using only the `sort_by` filter
|
||||
// does not provide the same results as `/movie/top_rated`. This appears to be adequate enough
|
||||
// to filter out extremely high-rated movies due to very little votes
|
||||
|
@ -530,7 +533,8 @@ namespace Ombi.Api.TheMovieDb
|
|||
var settings = await Settings;
|
||||
List<int> excludedGenres;
|
||||
|
||||
switch (media_type) {
|
||||
switch (media_type)
|
||||
{
|
||||
case "tv":
|
||||
excludedGenres = settings.ExcludedTvGenreIds;
|
||||
break;
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
"moment": "^2.29.1",
|
||||
"ng2-cookies": "^1.0.12",
|
||||
"ngx-clipboard": "^12.1.0",
|
||||
"ngx-infinite-scroll": "^9.0.0",
|
||||
"ngx-infinite-scroll": "^14.0.0",
|
||||
"ngx-moment": "^3.0.1",
|
||||
"ngx-order-pipe": "^2.2.0",
|
||||
"popper.js": "^1.14.3",
|
||||
|
|
|
@ -2,7 +2,13 @@
|
|||
<div *ngIf="loadingFlag" class="row justify-content-md-center top-spacing loading-spinner">
|
||||
<mat-spinner [color]="'accent'"></mat-spinner>
|
||||
</div>
|
||||
<div *ngIf="discoverResults.length > 0" class="row full-height discoverResults col" >
|
||||
|
||||
<div *ngIf="discoverResults.length > 0"
|
||||
class="row full-height discoverResults col"
|
||||
infiniteScroll
|
||||
[infiniteScrollDistance]="3"
|
||||
[infiniteScrollThrottle]="200"
|
||||
(scrolled)="onScroll()">
|
||||
<div id="searchResults" class="col-xl-2 col-lg-3 col-md-3 col-6 col-sm-4 small-padding" *ngFor="let result of discoverResults" data-test="searchResultsCount" attr.search-count="{{discoverResults.length}}">
|
||||
<discover-card [isAdmin]="isAdmin" [result]="result" [is4kEnabled]="is4kEnabled"></discover-card>
|
||||
</div>
|
||||
|
|
|
@ -29,6 +29,7 @@ export class DiscoverSearchResultsComponent implements OnInit {
|
|||
public filter: SearchFilter;
|
||||
|
||||
private isAdvancedSearch: boolean;
|
||||
private loadPosition: number = 30;
|
||||
|
||||
constructor(private searchService: SearchV2Service,
|
||||
private route: ActivatedRoute,
|
||||
|
@ -65,7 +66,7 @@ export class DiscoverSearchResultsComponent implements OnInit {
|
|||
}
|
||||
});
|
||||
|
||||
if (this.advancedDataService) {
|
||||
if (this.isAdvancedSearch) {
|
||||
return;
|
||||
}
|
||||
this.loadingFlag = true;
|
||||
|
@ -179,6 +180,31 @@ export class DiscoverSearchResultsComponent implements OnInit {
|
|||
});
|
||||
}
|
||||
|
||||
public onScroll() {
|
||||
console.log("scrolled");
|
||||
if (this.advancedDataService) {
|
||||
this.loadMoreAdvancedSearch();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private loadMoreAdvancedSearch() {
|
||||
const advancedOptions = this.advancedDataService.getOptions();
|
||||
|
||||
this.searchService.advancedSearch({
|
||||
type: advancedOptions.type == RequestType.movie ? "movie" : "tv",
|
||||
companies: advancedOptions.companies,
|
||||
genreIds: advancedOptions.genres,
|
||||
keywordIds : advancedOptions.keywords,
|
||||
releaseYear: advancedOptions.releaseYear,
|
||||
watchProviders: advancedOptions.watchProviders,
|
||||
}, this.loadPosition, 30).then(x => {
|
||||
|
||||
this.loadPosition += 30;
|
||||
this.mapAdvancedData(x);
|
||||
});
|
||||
}
|
||||
|
||||
private async search() {
|
||||
this.clear();
|
||||
this.results = await this.searchService
|
||||
|
|
|
@ -37,7 +37,7 @@ export class TvSearchComponent implements OnInit {
|
|||
private notificationService: NotificationService, private authService: AuthService,
|
||||
private imageService: ImageService, private sanitizer: DomSanitizer,
|
||||
@Inject(APP_BASE_HREF) href:string) {
|
||||
this.href = href;
|
||||
this.href = href;
|
||||
this.searchChanged.pipe(
|
||||
debounceTime(600), // Wait Xms after the last event before emitting last event
|
||||
distinctUntilChanged(), // only emit if value is different from previous value
|
||||
|
|
|
@ -8,7 +8,9 @@ import { RequestType } from "../../interfaces";
|
|||
export class AdvancedSearchDialogDataService {
|
||||
|
||||
@Output() public onDataChange = new EventEmitter<any>();
|
||||
@Output() public onOptionsChange = new EventEmitter<any>();
|
||||
private _data: any;
|
||||
private _options: any;
|
||||
private _type: RequestType;
|
||||
|
||||
setData(data: any, type: RequestType) {
|
||||
|
@ -17,10 +19,30 @@ export class AdvancedSearchDialogDataService {
|
|||
this.onDataChange.emit(this._data);
|
||||
}
|
||||
|
||||
setOptions(watchProviders: number[], genres: number[], keywords: number[], releaseYear: number, type: RequestType, position: number) {
|
||||
this._options = {
|
||||
watchProviders,
|
||||
genres,
|
||||
keywords,
|
||||
releaseYear,
|
||||
type,
|
||||
position
|
||||
};
|
||||
this.onOptionsChange.emit(this._options);
|
||||
}
|
||||
|
||||
getData(): any {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
getOptions(): any {
|
||||
return this._options;
|
||||
}
|
||||
|
||||
getLoaded(): number {
|
||||
return this._options.loaded;
|
||||
}
|
||||
|
||||
getType(): RequestType {
|
||||
return this._type;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
|
||||
|
||||
<div class="col-md-12">
|
||||
<mat-label>{{ "Search.YearOfRelease" | translate }}</mat-label>
|
||||
<genre-select [form]="form" [mediaType]="form.controls.type.value"></genre-select>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -49,7 +49,9 @@ export class AdvancedSearchDialogComponent implements OnInit {
|
|||
type: formData.type,
|
||||
}, 0, 30);
|
||||
|
||||
this.advancedSearchDialogService.setData(data, formData.type === 'movie' ? RequestType.movie : RequestType.tvShow);
|
||||
const type = formData.type === 'movie' ? RequestType.movie : RequestType.tvShow;
|
||||
this.advancedSearchDialogService.setData(data, type);
|
||||
this.advancedSearchDialogService.setOptions(watchProviderIds, genres, keywords, formData.releaseYear, type, 30);
|
||||
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
|
|
|
@ -1902,11 +1902,6 @@
|
|||
node-gyp "^8.4.1"
|
||||
read-package-json-fast "^2.0.3"
|
||||
|
||||
"@scarf/scarf@^1.1.0":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@scarf/scarf/-/scarf-1.1.1.tgz#d8b9f20037b3a37dbf8dcdc4b3b72f9285bfce35"
|
||||
integrity sha512-VGbKDbk1RFIaSmdVb0cNjjWJoRWRI/Weo23AjRCC2nryO0iAS8pzsToJfPVPtVs74WHw4L1UTADNdIYRLkirZQ==
|
||||
|
||||
"@schematics/angular@14.0.0":
|
||||
version "14.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-14.0.0.tgz#de6cb4c86586ed5b06adfd7a759cc9908e627787"
|
||||
|
@ -5619,13 +5614,12 @@ ngx-clipboard@^12.1.0:
|
|||
ngx-window-token "^2.0.0"
|
||||
tslib "^1.9.0"
|
||||
|
||||
ngx-infinite-scroll@^9.0.0:
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ngx-infinite-scroll/-/ngx-infinite-scroll-9.1.0.tgz#6716a47613ff59f236b85c3ce291b2fd57106824"
|
||||
integrity sha512-ZulbahgFsoPmP8cz7qPGDeFX9nKiSm74aav8vXNSI1ZoPiGYY5FQd8AK+yXqygY7tyCJRyt8Wp3DIg7zgP5dPA==
|
||||
ngx-infinite-scroll@^14.0.0:
|
||||
version "14.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ngx-infinite-scroll/-/ngx-infinite-scroll-14.0.0.tgz#395b15be5f451c3e3d2ad7ce2aeb66f8c66aba5d"
|
||||
integrity sha512-YZB5PBPXSERNtCGQRZTVflbgkh5asp01NPfC8KPItemmQik1Ip8ZCCbcyHA77TDTdilmaiu8TbguA3geg/LMWw==
|
||||
dependencies:
|
||||
"@scarf/scarf" "^1.1.0"
|
||||
opencollective-postinstall "^2.0.2"
|
||||
tslib "^2.3.0"
|
||||
|
||||
ngx-moment@^3.0.1:
|
||||
version "3.5.0"
|
||||
|
@ -5901,11 +5895,6 @@ open@8.4.0, open@^8.0.9:
|
|||
is-docker "^2.1.1"
|
||||
is-wsl "^2.2.0"
|
||||
|
||||
opencollective-postinstall@^2.0.2:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259"
|
||||
integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==
|
||||
|
||||
ora@5.4.1, ora@^5.4.1:
|
||||
version "5.4.1"
|
||||
resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18"
|
||||
|
|
|
@ -25,14 +25,14 @@
|
|||
{
|
||||
"episodeNumber": 1,
|
||||
"title": "Our Cup Runneth Over",
|
||||
"airDate": "2015-01-14T01:00:00+00:00",
|
||||
"airDate": "2015-01-13T00:00:00",
|
||||
"url": "https://www.tvmaze.com/episodes/153107/schitts-creek-1x01-our-cup-runneth-over",
|
||||
"available": false,
|
||||
"approved": false,
|
||||
"requested": false,
|
||||
"seasonId": 0,
|
||||
"season": null,
|
||||
"airDateDisplay": "01/14/2015 01:00:00",
|
||||
"airDateDisplay": "01/13/2015 00:00:00",
|
||||
"requestStatus": "",
|
||||
"id": 0
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue