diff --git a/.vscode/settings.json b/.vscode/settings.json index 3e8fb3f..19085ee 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,7 +14,7 @@ "source.fixAll.eslint": "explicit" }, "editor.cursorSmoothCaretAnimation": "on", - "editor.fontSize": 20, + "editor.fontSize": 15, "editor.minimap.enabled": false, "editor.tabSize": 2, @@ -65,8 +65,8 @@ "*.react.js": "javascriptreact", "*.jsx": "javascriptreact" }, - "terminal.integrated.fontSize": 20, + "terminal.integrated.fontSize": 15, "screencastMode.fontSize": 59, - "scm.inputFontSize": 20, + "scm.inputFontSize": 15, "typescript.tsdk": "node_modules\\typescript\\lib" } diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index d01c084..c1b3d38 100644 Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ diff --git a/package.json b/package.json index 3ce32ce..cdd2562 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,8 @@ "joi": "^17.13.3", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", - "ts-morph": "^22.0.0" + "ts-morph": "^22.0.0", + "zod": "^3.23.8" }, "devDependencies": { "@nestjs/cli": "^10.0.0", diff --git a/src/app.module.ts b/src/app.module.ts index 783f0b3..3b0754a 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -4,6 +4,7 @@ import { ConfigModule } from '@nestjs/config'; import { GraphQLModule } from '@nestjs/graphql'; import joi from 'joi'; +import { DiscoverFilteringModule } from './discover-filtering/discover-filtering.module'; import { DiscoverFormDataModule } from './discover-form-data/discover-form-data.module'; import { FilteringOptionsModule } from './filtering-options/filtering-options.module'; import { MovieModule } from './movie/movie.module'; @@ -26,6 +27,7 @@ import { ShowModule } from './show/show.module'; typePaths: [ // Enums used by all the graphql schemas './src/models/enum.graphql', + './src/models/Pagination.graphql', // Entertainment specific models, used for the Movie and Show schemas './src/models/entertainment/BelongsToCollection.graphql', @@ -52,7 +54,8 @@ import { ShowModule } from './show/show.module'; MovieModule, PersonModule, DiscoverFormDataModule, - FilteringOptionsModule + FilteringOptionsModule, + DiscoverFilteringModule ] }) export class AppModule {} diff --git a/src/discover-filtering/discover-filtering.module.ts b/src/discover-filtering/discover-filtering.module.ts new file mode 100644 index 0000000..10b41a2 --- /dev/null +++ b/src/discover-filtering/discover-filtering.module.ts @@ -0,0 +1,13 @@ +import { HttpModule } from '@nestjs/axios'; +import { Module } from '@nestjs/common'; + +import { DiscoverFilteringResolver } from './discover-filtering.resolver'; +import { DiscoverFilteringService } from './discover-filtering.service'; +import { FilteringOptionsModule } from '../filtering-options/filtering-options.module'; +import { UtilsModule } from '../utils/utils.module'; + +@Module({ + providers: [DiscoverFilteringService, DiscoverFilteringResolver], + imports: [FilteringOptionsModule, HttpModule, UtilsModule, FilteringOptionsModule] +}) +export class DiscoverFilteringModule {} diff --git a/src/discover-filtering/discover-filtering.resolver.ts b/src/discover-filtering/discover-filtering.resolver.ts new file mode 100644 index 0000000..51b23f9 --- /dev/null +++ b/src/discover-filtering/discover-filtering.resolver.ts @@ -0,0 +1,25 @@ +import { Args, Query, Resolver } from '@nestjs/graphql'; + +import { DiscoverFilteringService } from './discover-filtering.service'; +import { DiscoverFormDataInput } from '../graphql.schema'; + +@Resolver() +export class DiscoverFilteringResolver { + constructor(private readonly discoverFilteringService: DiscoverFilteringService) {} + + @Query() + discoverMovies( + @Args('filterValues') filters: DiscoverFormDataInput, + @Args('pageNumber') pageNumber: number + ) { + return this.discoverFilteringService.getDiscoverMovies({ filters, pageNumber }); + } + + @Query() + discoverShows( + @Args('filterValues') filters: DiscoverFormDataInput, + @Args('pageNumber') pageNumber: number + ) { + return this.discoverFilteringService.getDiscoverShows({ filters, pageNumber }); + } +} diff --git a/src/discover-filtering/discover-filtering.service.ts b/src/discover-filtering/discover-filtering.service.ts new file mode 100644 index 0000000..9d97944 --- /dev/null +++ b/src/discover-filtering/discover-filtering.service.ts @@ -0,0 +1,320 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { HttpService } from '@nestjs/axios'; +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { firstValueFrom } from 'rxjs'; + +import { FilteringOptionsService } from '../filtering-options/filtering-options.service'; +import { + DiscoverFormData, + DiscoverFormDataInput, + DiscoverResult, + ENTERTAINMENT_TYPES, + PaginatedDiscoverResult +} from '../graphql.schema'; +import { isNonNullable } from '../lib'; +import { Pagination } from '../models/Pagination'; +import { ITheOpenMovieDatabaseDiscoverResult } from '../types/Discover'; +import { Nullable } from '../types/Nullable'; +import { UtilsService } from '../utils/utils.service'; + +@Injectable() +export class DiscoverFilteringService { + constructor( + private readonly httpService: HttpService, + private readonly configService: ConfigService, + private readonly utilService: UtilsService, + private readonly filteringOptionsService: FilteringOptionsService + ) {} + + private handleMultiFilter({ + operation = 'OR', + filterValues = [], + searchParameter + }: { + operation?: 'AND' | 'OR'; + filterValues: null | undefined | Array>; + searchParameter: string; + }) { + const noneNullValues = filterValues?.filter(isNonNullable) ?? []; + + if (noneNullValues.length === 0) return ''; + + if (operation === 'OR') { + return `${searchParameter}=${noneNullValues.join('|')}`; + } + + return `${searchParameter}=${noneNullValues.join(',')}`; + } + + private handleSearchParameterSetting( + searchParameters: URLSearchParams, + key: string, + value: null | string | undefined + ) { + if (value) { + searchParameters.set(key, value); + } + } + + public getDiscoverUrlSearchParameters({ + filters, + entertainmentType + }: { + filters: DiscoverFormDataInput; + entertainmentType: ENTERTAINMENT_TYPES; + }) { + const searchParameters = new URLSearchParams(); + + // Certificate filter + this.handleSearchParameterSetting( + searchParameters, + 'certifications', + this.handleMultiFilter({ + filterValues: filters.certifications, + searchParameter: 'certifications' + }) + ); + + // Original language filter + this.handleSearchParameterSetting( + searchParameters, + 'with_original_language', + filters.with_original_language + ); + + // Region filter + this.handleSearchParameterSetting(searchParameters, 'region', filters.region); + + // Watch monetization types filter e.g. flatrate, free, adds, rent, buy etc + this.handleSearchParameterSetting( + searchParameters, + 'with_watch_monetization_types', + this.handleMultiFilter({ + filterValues: + filters.with_watch_monetization_types?.length === 1 && + filters.with_watch_monetization_types[0] === 'all' + ? this.filteringOptionsService + .getAvailabilityOptions({ + excludeOptions: ['all'] + }) + .map((option) => option.value) + : filters.with_watch_monetization_types, + searchParameter: 'with_watch_monetization_types' + }) + ); + + // Genre filter + this.handleSearchParameterSetting( + searchParameters, + 'with_genres', + this.handleMultiFilter({ + filterValues: filters.with_genres, + searchParameter: 'with_genres' + }) + ); + + // Sort by filter e.g. popularity.desc, vote_average.desc, first_air_date.desc, release_date.desc + this.handleSearchParameterSetting(searchParameters, 'sort_by', filters.sort_by); + + // Release type filter e.g. 1, 2, 3, 4, 5, 6 etc + this.handleSearchParameterSetting( + searchParameters, + 'with_release_types', + this.handleMultiFilter({ + filterValues: + filters.with_release_types?.length === 1 && filters.with_release_types[0] === 'all' + ? this.filteringOptionsService + .getReleaseTypeOptions({ + excludeOptions: ['all'] + }) + .map((option) => option.value) + : filters.with_release_types, + searchParameter: 'with_release_types' + }) + ); + + // Vote count filter e.g. vote_average.gte, vote_average.lte + this.handleSearchParameterSetting( + searchParameters, + 'vote_average.gte', + filters.vote_count?.gte?.toString() + ); + + this.handleSearchParameterSetting( + searchParameters, + 'vote_average.lte', + filters.vote_count?.lte?.toString() + ); + + // OTT provider filter e.g. netflix, disney, amazon_prime, hulu, apple_tv, etc + this.handleSearchParameterSetting( + searchParameters, + 'with_watch_providers', + this.handleMultiFilter({ + filterValues: filters.with_ott_providers, + + searchParameter: 'with_watch_providers' + }) + ); + + // If the user is using the "search first air date" filter then instead of using "release_date" we use "first_air_date" + if (entertainmentType === ENTERTAINMENT_TYPES.TV && filters.search_first_air_date) { + this.handleSearchParameterSetting( + searchParameters, + 'first_air_date.gte', + filters.air_date?.gte + ); + + this.handleSearchParameterSetting( + searchParameters, + 'first_air_date.lte', + filters.air_date?.lte + ); + } else { + this.handleSearchParameterSetting( + searchParameters, + 'release_date.gte', + filters.release_date?.gte + ); + + this.handleSearchParameterSetting( + searchParameters, + 'release_date.lte', + filters.release_date?.lte + ); + } + + // Genre filter e.g. 28,16,35,80,99 (Ids that represent genres) + this.handleSearchParameterSetting( + searchParameters, + 'with_genres', + this.handleMultiFilter({ + filterValues: filters.with_genres, + + searchParameter: 'with_genres' + }) + ); + + /* + Runtime filter e.g. with_runtime.gte, with_runtime.lte + + NOTE: + - We currently only use the "gte" filter due to limited FE controls + - TODO: Add support for the "lte" filter via the TanStack range slider library + */ + + this.handleSearchParameterSetting( + searchParameters, + 'with_runtime.gte', + filters.with_runtime?.gte?.toString() + ); + + this.handleSearchParameterSetting( + searchParameters, + 'with_runtime.lte', + filters.with_runtime?.lte?.toString() + ); + + // Release type filter e.g. 1, 2, 3, 4, 5, 6 (Ids that represent release types e.g. Theatrical, TV, Streaming etc) + + this.handleSearchParameterSetting( + searchParameters, + 'with_release_types', + this.handleMultiFilter({ + filterValues: filters.with_release_types, + searchParameter: 'with_release_types' + }) + ); + + return searchParameters; + } + + private async discoverResource({ + filters, + entertainmentType, + pageNumber + }: { + filters: DiscoverFormData; + entertainmentType: ENTERTAINMENT_TYPES; + pageNumber: number; + }): Promise> { + const discoverSearchParameters = this.getDiscoverUrlSearchParameters({ + filters, + entertainmentType + }); + + const { data: discoverData } = await firstValueFrom( + this.httpService.get<{ + results: Array< + ITheOpenMovieDatabaseDiscoverResult & { + first_air_date: Nullable; + release_date: Nullable; + } + >; + total_pages: number; + total_results: number; + }>( + `https://api.themoviedb.org/3/discover/${entertainmentType.toLocaleLowerCase()}?include_adult=false&include_video=false&language=en-US&page=${pageNumber}${discoverSearchParameters.toString()}`, + { + headers: { + Accept: 'application/json', + Authorization: `Bearer ${this.configService.get('THE_OPEN_MOVIE_DATABASE_API_KEY')}` + } + } + ) + ); + + return new Pagination( + discoverData.results.map((result) => ({ + adult: result.adult, + backdropUrl: this.utilService.getFullImageUrlPath(result.backdrop_path), + posterUrl: this.utilService.getFullImageUrlPath(result.poster_path), + name: result.title.length === 0 ? result.original_title : result.title, + homepage: result.homepage, + id: result.id.toString(), + originCountry: result.origin_country, + originalLanguage: result.original_language, + overview: result.overview, + popularity: result.popularity, + posterPath: result.poster_path, + status: result.status, + tagline: result.tagline, + voteAverage: result.vote_average, + voteCount: result.vote_count, + releaseDate: result.release_date ?? result.first_air_date + })), + pageNumber, + discoverData.total_pages, + discoverData.total_results + ); + } + + public async getDiscoverShows({ + filters, + pageNumber + }: { + filters: DiscoverFormDataInput; + pageNumber: number; + }): Promise { + return this.discoverResource({ + entertainmentType: ENTERTAINMENT_TYPES.TV, + filters, + pageNumber + }); + } + + public async getDiscoverMovies({ + filters, + pageNumber + }: { + filters: DiscoverFormDataInput; + pageNumber: number; + }): Promise { + return this.discoverResource({ + entertainmentType: ENTERTAINMENT_TYPES.MOVIE, + filters, + pageNumber + }); + } +} diff --git a/src/discover-form-data/discover-form-data.service.ts b/src/discover-form-data/discover-form-data.service.ts index 4a775dc..1aa4ceb 100644 --- a/src/discover-form-data/discover-form-data.service.ts +++ b/src/discover-form-data/discover-form-data.service.ts @@ -1,6 +1,9 @@ import { Injectable } from '@nestjs/common'; import { addDays, format, subDays } from 'date-fns'; +import { FilteringOptionsService } from '../filtering-options/filtering-options.service'; +import { ENTERTAINMENT_TYPES, RESOURCE_TYPE } from '../graphql.schema'; +import { isNonNullable } from '../lib'; import { GetCheckboxFormDataResult, GetDropdownFormDataOptions, @@ -19,15 +22,8 @@ import { GetSortSectionFormDataResult, GetDiscoverFormDataOptions, GetDiscoverFormDataResult, - GetCheckboxFormDataOptions, - GetAirDateFormDataOptions, - GetReleaseDateOptions, - GetReleaseDateResult, - GetAirDateFormDataResult -} from './types'; -import { FilteringOptionsService } from '../filtering-options/filtering-options.service'; -import { ENTERTAINMENT_TYPES, RESOURCE_TYPE } from '../graphql.schema'; -import { isNonNullable } from '../lib'; + GetCheckboxFormDataOptions +} from '../types/Discover'; @Injectable() export class DiscoverFormDataService { @@ -105,24 +101,34 @@ export class DiscoverFormDataService { }; }; - private getAirDateFormData = ({ - entertainmentType, + private setupDateFilters = ({ resourceType, + mediaType, searchFirstAirDate - }: GetAirDateFormDataOptions): GetAirDateFormDataResult => { - // The Release Date field isn't a valid entertainmentType for 'movie' - if (entertainmentType === ENTERTAINMENT_TYPES.MOVIE) { + }: { + resourceType: RESOURCE_TYPE; + mediaType: ENTERTAINMENT_TYPES; + searchFirstAirDate: boolean; + }) => { + // gte - "From" + // lte - "To" + + // Set the release_date values for the /movie/popular route + if (resourceType === RESOURCE_TYPE.POPULAR && mediaType === ENTERTAINMENT_TYPES.MOVIE) { return { - lte: null, - gte: null + release_date: { + gte: '', + lte: format(addDays(new Date(), 181), 'yyyy-MM-dd') + }, + air_date: { + gte: null, + lte: null + } }; } - // gte - "From" - // lte - "To" - // Set the air_date values for the /tv/popular route - if (resourceType === RESOURCE_TYPE.POPULAR) { + if (resourceType === RESOURCE_TYPE.POPULAR && mediaType === ENTERTAINMENT_TYPES.TV) { // Get the "From" value (gte) const gte = ''; @@ -131,19 +137,45 @@ export class DiscoverFormDataService { if (searchFirstAirDate) { return { - gte, - lte + air_date: { + gte, + lte + }, + release_date: { + gte: null, + lte: null + } }; } return { - gte, - lte + air_date: { + gte: null, + lte: null + }, + release_date: { + gte, + lte + } + }; + } + + // Set the release_date values for the /tv/now-playing route + if (resourceType === RESOURCE_TYPE.NOW_PLAYING && mediaType === ENTERTAINMENT_TYPES.MOVIE) { + return { + air_date: { + gte: null, + lte: null + }, + release_date: { + gte: format(subDays(new Date(), 40), 'yyyy-MM-dd'), + lte: format(addDays(new Date(), 2), 'yyyy-MM-dd') + } }; } // Set the air_date values for the /tv/airing-today route - if (resourceType === RESOURCE_TYPE.AIRING_TODAY) { + if (resourceType === RESOURCE_TYPE.AIRING_TODAY && mediaType === ENTERTAINMENT_TYPES.TV) { // Get the "From" value (gte) const gte = format(new Date(), 'yyyy-MM-dd'); @@ -154,19 +186,45 @@ export class DiscoverFormDataService { // NOTE: Add find out more about this filter's functionality if (searchFirstAirDate) { return { - gte, - lte + air_date: { + gte, + lte + }, + release_date: { + gte: null, + lte: null + } }; } return { - gte, - lte + release_date: { + gte, + lte + }, + air_date: { + gte: null, + lte: null + } + }; + } + + // Set the release_date values for the /movie/upcoming route + if (resourceType === RESOURCE_TYPE.UPCOMING && mediaType === ENTERTAINMENT_TYPES.MOVIE) { + return { + release_date: { + gte: format(addDays(new Date(), 3), 'yyyy-MM-dd'), + lte: format(addDays(new Date(), 24), 'yyyy-MM-dd') + }, + air_date: { + gte: null, + lte: null + } }; } // Set the air_date values for the /tv/on-the-air route - if (resourceType === RESOURCE_TYPE.ON_THE_AIR) { + if (resourceType === RESOURCE_TYPE.ON_THE_AIR && mediaType === ENTERTAINMENT_TYPES.TV) { // Get the "From" value (gte) const gte = format(new Date(), 'yyyy-MM-dd'); @@ -177,98 +235,76 @@ export class DiscoverFormDataService { // NOTE: Add find out more about this filter's functionality if (searchFirstAirDate) { return { - gte, - lte + air_date: { + gte, + lte + }, + release_date: { + gte: null, + lte: null + } }; } return { - gte, - lte - }; - } - - if (resourceType === RESOURCE_TYPE.TOP_RATED) { - // Set the release_date values for the /tv/top-rated route - // Get the "From" value (gte) - const gte = ''; - - // Get the "To" value (lte) - const lte = format(addDays(new Date(), 180), 'yyyy-MM-dd'); - - // When using the first_air fate filter set the air_date values otherwise just set the release_date - // NOTE: Add find out more about this filter's functionality - if (searchFirstAirDate) { - return { + release_date: { gte, lte - }; - } - - return { - gte, - lte + }, + air_date: { + gte: null, + lte: null + } }; } - return { - gte: null, - lte: null - }; - }; - - private getReleaseDateFormData({ - entertainmentType, - resourceType - }: GetReleaseDateOptions): GetReleaseDateResult { - // The Release Date field isn't a valid entertainmentType for 'tv' - if (entertainmentType === ENTERTAINMENT_TYPES.TV) { - return { - lte: null, - gte: null - }; - } - - // gte - "From" - // lte - "To" - - // Set the release_date values for the /movie/popular route - if (resourceType === RESOURCE_TYPE.POPULAR) { + // Set the release_date values for the /movie/top-rated route + if (resourceType === RESOURCE_TYPE.TOP_RATED && mediaType === ENTERTAINMENT_TYPES.MOVIE) { return { - gte: '', - lte: format(addDays(new Date(), 181), 'yyyy-MM-dd') // Add 181 days to the "To" label value + release_date: { + gte: '', + lte: format(addDays(new Date(), 181), 'yyyy-MM-dd') + }, + air_date: { + gte: null, + lte: null + } }; } - // Set the release_date values for the /tv/now-playing route - if (resourceType === RESOURCE_TYPE.NOW_PLAYING) { - return { - gte: format(subDays(new Date(), 40), 'yyyy-MM-dd'), - lte: format(addDays(new Date(), 2), 'yyyy-MM-dd') - }; - } + // Set the release_date values for the /tv/top-rated route + // Get the "From" value (gte) + const gte = ''; - // Set the release_date values for the /movie/upcoming route - if (resourceType === RESOURCE_TYPE.UPCOMING) { - return { - gte: format(addDays(new Date(), 3), 'yyyy-MM-dd'), - lte: format(addDays(new Date(), 24), 'yyyy-MM-dd') - }; - } + // Get the "To" value (lte) + const lte = format(addDays(new Date(), 180), 'yyyy-MM-dd'); - // Set the release_date values for the /movie/top-rated route - if (resourceType === RESOURCE_TYPE.TOP_RATED) { + // When using the first_air fate filter set the air_date values otherwise just set the release_date + // NOTE: Add find out more about this filter's functionality + if (searchFirstAirDate) { return { - gte: '', - lte: format(addDays(new Date(), 181), 'yyyy-MM-dd') + air_date: { + gte, + lte + }, + release_date: { + gte: null, + lte: null + } }; } return { - gte: null, - lte: null + release_date: { + gte, + lte + }, + air_date: { + gte: null, + lte: null + } }; - } + }; private getVoteAverageFilterFormData({ voteAverageGte = 10, @@ -446,25 +482,20 @@ export class DiscoverFormDataService { searchFirstAirDate = false; } - const airDate = this.getAirDateFormData({ - entertainmentType, + const dateFilters = this.setupDateFilters({ resourceType, + mediaType: entertainmentType, searchFirstAirDate }); - const releaseDate = this.getReleaseDateFormData({ - entertainmentType, - resourceType - }); - return { show_me: showMe, with_watch_monetization_types: withWatchMonetizationTypes, with_genres: withGenres.isMultiple ? withGenres.value : [], certifications: certification.isMultiple ? certification.value : [], with_release_types: withReleaseTypes, - release_date: releaseDate, - air_date: airDate, + release_date: dateFilters.release_date, + air_date: dateFilters.air_date, with_original_language: !withOriginalLanguage.isMultiple ? withOriginalLanguage.value : null, region: !region.isMultiple ? region.value : null, vote_average: voteAverage, @@ -513,9 +544,9 @@ export class DiscoverFormDataService { let defaultSortBy = 'popularity.desc'; - if (typeof defaultValues?.sort !== 'undefined' && defaultValues.sort !== null) { + if (typeof defaultValues?.sort_by !== 'undefined' && defaultValues.sort_by !== null) { // If there is an existing value use that - defaultSortBy = defaultValues.sort; + defaultSortBy = defaultValues.sort_by; } else if (resourceType === RESOURCE_TYPE.TOP_RATED) { // If there is no value but the resource type is "top-rated" then sort the results by vote_average in descending order defaultSortBy = 'vote_average.desc'; @@ -530,12 +561,12 @@ export class DiscoverFormDataService { if (!generatedSortByValue.isMultiple) { return { - sort: generatedSortByValue.value + sort_by: generatedSortByValue.value }; } return { - sort: null + sort_by: null }; } diff --git a/src/discover-form-data/discover-form-data.ts b/src/discover-form-data/discover-form-data.ts deleted file mode 100644 index d3e73bf..0000000 --- a/src/discover-form-data/discover-form-data.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -export interface IDefaultValues { - sort_by?: string; - ott_region?: string; - restrict_services?: boolean; - with_ott_providers?: Array; - show_me?: string; - with_watch_monetization_types?: Array; - with_genres?: Array; - certifications?: Array; - with_release_types?: Array; - with_original_language?: string; - region?: string; - vote_average_lte?: string; - vote_average_gte?: string; - with_runtime_lte?: string; - with_runtime_gte?: string; - vote_count_lte?: string; - vote_count_gte?: string; - search_first_air_date?: boolean; -} diff --git a/src/entertainment/entertainment.service.ts b/src/entertainment/entertainment.service.ts index ec40145..7424288 100644 --- a/src/entertainment/entertainment.service.ts +++ b/src/entertainment/entertainment.service.ts @@ -20,11 +20,12 @@ import { Social, Video } from '../graphql.schema'; -import { - TheOpenMovieDatabaseBelongsToCollection, - TheOpenMovieDatabaseSpokenLanguages -} from '../movie/movie'; import { SocialsService } from '../socials/socials.service'; +import { Nullable } from '../types/Nullable'; +import { + ISpokenLanguage, + ITheOpenMovieDatabaseBelongsToCollection +} from '../types/TheOpenMovieDatabase.common'; import { UtilsService } from '../utils/utils.service'; // eslint-disable-next-line @typescript-eslint/naming-convention @@ -45,7 +46,7 @@ export class EntertainmentService { async getReview({ entertainmentType, entertainmentId - }: IEntertainmentCommonArguments): Promise { + }: IEntertainmentCommonArguments): Promise> { const { data } = await firstValueFrom( this.httpService.get( `https://api.themoviedb.org/3/${entertainmentType.toLowerCase()}/${entertainmentId}/reviews?language=en-U`, @@ -85,7 +86,7 @@ export class EntertainmentService { async getTopBilledCast({ entertainmentType, entertainmentId - }: IEntertainmentCommonArguments): Promise | null> { + }: IEntertainmentCommonArguments): Promise> { const { data } = await firstValueFrom( this.httpService.get( `https://api.themoviedb.org/3/${entertainmentType.toLocaleLowerCase()}/${entertainmentId}/credits?language=en-U`, @@ -113,7 +114,7 @@ export class EntertainmentService { async getFeaturedCrewMembers({ entertainmentType, entertainmentId - }: IEntertainmentCommonArguments): Promise | null> { + }: IEntertainmentCommonArguments): Promise> { const { data } = await firstValueFrom( this.httpService.get( `https://api.themoviedb.org/3/${entertainmentType.toLowerCase()}/${entertainmentId}/credits?language=en-U`, @@ -172,7 +173,7 @@ export class EntertainmentService { async getKeywords({ entertainmentType, entertainmentId - }: IEntertainmentCommonArguments): Promise | null> { + }: IEntertainmentCommonArguments): Promise> { const { data } = await firstValueFrom( this.httpService.get( `https://api.themoviedb.org/3/${entertainmentType.toLocaleLowerCase()}/${entertainmentId}/keywords?language=en-U`, @@ -199,14 +200,19 @@ export class EntertainmentService { } getCollection = ( - collection: undefined | null | TheOpenMovieDatabaseBelongsToCollection = null - ): BelongsToCollection | null => { + collection: Nullable = null + ): Nullable => { if (collection !== null) { + // id: number; + // name: string; + // backdrop: string; + // backgroundUrl?: Nullable; + // posterUrl?: Nullable; return { - backgroundUrl: this.utilService.getFullImageUrlPath(collection.poster_path), id: collection.id, name: collection.name, - posterUrl: collection.poster_path + posterUrl: collection.poster_path, + backgroundUrl: this.utilService.getFullImageUrlPath(collection.poster_path) }; } @@ -218,7 +224,7 @@ export class EntertainmentService { spokenLanguages }: { originalLanguage: string; - spokenLanguages: Array; + spokenLanguages: Array; }) => { if (originalLanguage.length !== 0 && spokenLanguages.length === 0) { const foundFriendlyLanguage = spokenLanguages.find( @@ -238,7 +244,7 @@ export class EntertainmentService { async getYouTubeVideo({ entertainmentType, entertainmentId - }: IEntertainmentCommonArguments): Promise