From 898bc89fa78245c1f3de9481f6c724f087a16e39 Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 22 Jul 2022 22:04:18 +0100 Subject: [PATCH] =?UTF-8?q?feat(discover):=20=E2=9C=A8=20Added=20infinite?= =?UTF-8?q?=20scroll=20on=20advanced=20search=20results?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(discover): :sparkles: Added infinite scroll on advanced search results --- .../Engine/V2/MovieSearchEngineV2.cs | 14 +++++----- src/Ombi.TheMovieDbApi/IMovieDbApi.cs | 2 +- src/Ombi.TheMovieDbApi/TheMovieDbApi.cs | 20 +++++++------ src/Ombi/ClientApp/package.json | 2 +- .../search-results.component.html | 8 +++++- .../search-results.component.ts | 28 ++++++++++++++++++- .../src/app/search/tvsearch.component.ts | 2 +- .../advanced-search-dialog-data.service.ts | 22 +++++++++++++++ .../advanced-search-dialog.component.html | 1 - .../advanced-search-dialog.component.ts | 4 ++- src/Ombi/ClientApp/yarn.lock | 21 ++++---------- .../fixtures/api/v1/tv-search-extra-info.json | 4 +-- 12 files changed, 88 insertions(+), 40 deletions(-) diff --git a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs index ab78231a8..044d79969 100644 --- a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs @@ -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(); - //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); } /// diff --git a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs index 12f5255b4..9b277c091 100644 --- a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs @@ -45,6 +45,6 @@ namespace Ombi.Api.TheMovieDb Task> GetGenres(string media, CancellationToken cancellationToken, string languageCode); Task> GetLanguages(CancellationToken cancellationToken); Task> SearchWatchProviders(string media, string searchTerm, CancellationToken cancellationToken); - Task> AdvancedSearch(DiscoverModel model, CancellationToken cancellationToken); + Task> AdvancedSearch(DiscoverModel model, int page, CancellationToken cancellationToken); } } diff --git a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs index 5b0817388..055265701 100644 --- a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs @@ -70,11 +70,11 @@ namespace Ombi.Api.TheMovieDb - public async Task> AdvancedSearch(DiscoverModel model, CancellationToken cancellationToken) + public async Task> 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>(request, cancellationToken); return Mapper.Map>(result.results); } @@ -139,7 +142,7 @@ namespace Ombi.Api.TheMovieDb var result = await Api.Request(request); return result; } - + public async Task 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>(request); return Mapper.Map>(result.results); } - + public Task> 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 excludedGenres; - switch (media_type) { + switch (media_type) + { case "tv": excludedGenres = settings.ExcludedTvGenreIds; break; diff --git a/src/Ombi/ClientApp/package.json b/src/Ombi/ClientApp/package.json index a07dc81c4..a995527fd 100644 --- a/src/Ombi/ClientApp/package.json +++ b/src/Ombi/ClientApp/package.json @@ -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", diff --git a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html index b1911ceee..97ff038a0 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html +++ b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html @@ -2,7 +2,13 @@
-
+ +
diff --git a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts index a8544365d..295902927 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts @@ -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 diff --git a/src/Ombi/ClientApp/src/app/search/tvsearch.component.ts b/src/Ombi/ClientApp/src/app/search/tvsearch.component.ts index 5f65f3192..dc87f0800 100644 --- a/src/Ombi/ClientApp/src/app/search/tvsearch.component.ts +++ b/src/Ombi/ClientApp/src/app/search/tvsearch.component.ts @@ -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 diff --git a/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog-data.service.ts b/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog-data.service.ts index c6312915a..480751e78 100644 --- a/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog-data.service.ts +++ b/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog-data.service.ts @@ -8,7 +8,9 @@ import { RequestType } from "../../interfaces"; export class AdvancedSearchDialogDataService { @Output() public onDataChange = new EventEmitter(); + @Output() public onOptionsChange = new EventEmitter(); 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; } diff --git a/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog.component.html b/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog.component.html index b0ff4a81e..d5bf4defc 100644 --- a/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog.component.html +++ b/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog.component.html @@ -33,7 +33,6 @@
- {{ "Search.YearOfRelease" | translate }}
diff --git a/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog.component.ts b/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog.component.ts index 3aa004261..7e58b3790 100644 --- a/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog.component.ts +++ b/src/Ombi/ClientApp/src/app/shared/advanced-search-dialog/advanced-search-dialog.component.ts @@ -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); } diff --git a/src/Ombi/ClientApp/yarn.lock b/src/Ombi/ClientApp/yarn.lock index 7c65cc425..cc330dd36 100644 --- a/src/Ombi/ClientApp/yarn.lock +++ b/src/Ombi/ClientApp/yarn.lock @@ -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" diff --git a/tests/cypress/fixtures/api/v1/tv-search-extra-info.json b/tests/cypress/fixtures/api/v1/tv-search-extra-info.json index 03b894eb2..3421514a2 100644 --- a/tests/cypress/fixtures/api/v1/tv-search-extra-info.json +++ b/tests/cypress/fixtures/api/v1/tv-search-extra-info.json @@ -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 },