refactor: move genre/studio/network calls into their own endpoints

this commit also adds a `useDiscover` hook to help with creating discover pages with less repeating
code
This commit is contained in:
sct
2021-03-04 10:18:46 +00:00
parent ed0a7fbdf5
commit 63c122e5e0
23 changed files with 885 additions and 365 deletions

View File

@@ -3,7 +3,6 @@ import cacheManager from '../../lib/cache';
import ExternalAPI from '../externalapi';
import {
TmdbCollection,
TmdbStudio,
TmdbExternalIdResponse,
TmdbGenre,
TmdbGenresResult,
@@ -19,6 +18,7 @@ import {
TmdbSeasonWithEpisodes,
TmdbTvDetails,
TmdbUpcomingMoviesResponse,
TmdbProductionCompany,
} from './interfaces';
interface SearchOptions {
@@ -675,9 +675,11 @@ class TheMovieDb extends ExternalAPI {
}
}
public async getStudio(studioId: number): Promise<TmdbStudio> {
public async getStudio(studioId: number): Promise<TmdbProductionCompany> {
try {
const data = await this.get<TmdbStudio>(`/company/${studioId}`);
const data = await this.get<TmdbProductionCompany>(
`/company/${studioId}`
);
return data;
} catch (e) {
@@ -695,11 +697,19 @@ class TheMovieDb extends ExternalAPI {
}
}
public async getMovieGenres(): Promise<TmdbGenre[]> {
public async getMovieGenres({
language = 'en',
}: {
language?: string;
} = {}): Promise<TmdbGenre[]> {
try {
const data = await this.get<TmdbGenresResult>(
'/genre/movie/list',
{},
{
params: {
language,
},
},
86400 // 24 hours
);
@@ -711,11 +721,19 @@ class TheMovieDb extends ExternalAPI {
}
}
public async getTvGenres(): Promise<TmdbGenre[]> {
public async getTvGenres({
language = 'en',
}: {
language?: string;
} = {}): Promise<TmdbGenre[]> {
try {
const data = await this.get<TmdbGenresResult>(
'/genre/tv/list',
{},
{
params: {
language,
},
},
86400 // 24 hours
);

View File

@@ -109,6 +109,16 @@ export interface TmdbExternalIds {
twitter_id?: string;
}
export interface TmdbProductionCompany {
id: number;
logo_path?: string;
name: string;
origin_country: string;
homepage?: string;
headquarters?: string;
description?: string;
}
export interface TmdbMovieDetails {
id: number;
imdb_id?: string;
@@ -125,12 +135,7 @@ export interface TmdbMovieDetails {
original_title: string;
overview?: string;
popularity: number;
production_companies: {
id: number;
name: string;
logo_path?: string;
origin_country: string;
}[];
production_companies: TmdbProductionCompany[];
production_countries: {
iso_3166_1: string;
name: string;
@@ -227,12 +232,7 @@ export interface TmdbTvDetails {
last_episode_to_air?: TmdbTvEpisodeResult;
name: string;
next_episode_to_air?: TmdbTvEpisodeResult;
networks: {
id: number;
name: string;
logo_path: string;
origin_country: string;
}[];
networks: TmdbNetwork[];
number_of_episodes: number;
number_of_seasons: number;
origin_country: string[];
@@ -391,17 +391,6 @@ export interface TmdbGenre {
name: string;
}
export interface TmdbStudio {
id: number;
name: string;
description?: string;
headquarters?: string;
homepage?: string;
logo_path?: string;
origin_country?: string;
parent_company?: TmdbStudio;
}
export interface TmdbNetwork {
id: number;
name: string;

View File

@@ -1,6 +1,7 @@
import type {
TmdbMovieDetails,
TmdbMovieReleaseResult,
TmdbProductionCompany,
} from '../api/themoviedb/interfaces';
import {
ProductionCompany,
@@ -79,6 +80,18 @@ export interface MovieDetails {
plexUrl?: string;
}
export const mapProductionCompany = (
company: TmdbProductionCompany
): ProductionCompany => ({
id: company.id,
name: company.name,
originCountry: company.origin_country,
description: company.description,
headquarters: company.headquarters,
homepage: company.homepage,
logoPath: company.logo_path,
});
export const mapMovieDetails = (
movie: TmdbMovieDetails,
media?: Media
@@ -91,12 +104,7 @@ export const mapMovieDetails = (
originalLanguage: movie.original_language,
originalTitle: movie.original_title,
popularity: movie.popularity,
productionCompanies: movie.production_companies.map((company) => ({
id: company.id,
logoPath: company.logo_path,
originCountry: company.origin_country,
name: company.name,
})),
productionCompanies: movie.production_companies.map(mapProductionCompany),
productionCountries: movie.production_countries,
releaseDate: movie.release_date,
releases: movie.release_dates,

View File

@@ -9,6 +9,7 @@ import {
mapExternalIds,
Keyword,
mapVideos,
TvNetwork,
} from './common';
import type {
TmdbTvEpisodeResult,
@@ -16,6 +17,7 @@ import type {
TmdbTvDetails,
TmdbSeasonWithEpisodes,
TmdbTvRatingResult,
TmdbNetwork,
} from '../api/themoviedb/interfaces';
import type Media from '../entity/Media';
import { Video } from './Movie';
@@ -77,7 +79,7 @@ export interface TvDetails {
lastEpisodeToAir?: Episode;
name: string;
nextEpisodeToAir?: Episode;
networks: ProductionCompany[];
networks: TvNetwork[];
numberOfEpisodes: number;
numberOfSeasons: number;
originCountry: string[];
@@ -139,6 +141,15 @@ export const mapSeasonWithEpisodes = (
posterPath: season.poster_path,
});
export const mapNetwork = (network: TmdbNetwork): TvNetwork => ({
id: network.id,
name: network.name,
originCountry: network.origin_country,
headquarters: network.headquarters,
homepage: network.homepage,
logoPath: network.logo_path,
});
export const mapTvDetails = (
show: TmdbTvDetails,
media?: Media
@@ -157,12 +168,7 @@ export const mapTvDetails = (
languages: show.languages,
lastAirDate: show.last_air_date,
name: show.name,
networks: show.networks.map((network) => ({
id: network.id,
name: network.name,
originCountry: network.origin_country,
logoPath: network.logo_path,
})),
networks: show.networks.map(mapNetwork),
numberOfEpisodes: show.number_of_episodes,
numberOfSeasons: show.number_of_seasons,
originCountry: show.origin_country,

View File

@@ -14,6 +14,18 @@ export interface ProductionCompany {
logoPath?: string;
originCountry: string;
name: string;
description?: string;
headquarters?: string;
homepage?: string;
}
export interface TvNetwork {
id: number;
logoPath?: string;
originCountry?: string;
name: string;
headquarters?: string;
homepage?: string;
}
export interface Keyword {

View File

@@ -6,6 +6,8 @@ import { isMovie, isPerson } from '../utils/typeHelpers';
import { MediaType } from '../constants/media';
import { getSettings } from '../lib/settings';
import { User } from '../entity/User';
import { mapProductionCompany } from '../models/Movie';
import { mapNetwork } from '../models/Tv';
const createTmdbWithRegionLanaguage = (user?: User): TheMovieDb => {
const settings = getSettings();
@@ -61,6 +63,82 @@ discoverRoutes.get('/movies', async (req, res) => {
});
});
discoverRoutes.get<{ genreId: string }>(
'/movies/genre/:genreId',
async (req, res) => {
const tmdb = createTmdbWithRegionLanaguage(req.user);
const genres = await tmdb.getMovieGenres({
language: req.query.language as string,
});
const genre = genres.find(
(genre) => genre.id === Number(req.params.genreId)
);
const data = await tmdb.getDiscoverMovies({
page: Number(req.query.page),
language: req.query.language as string,
genre: Number(req.params.genreId),
});
const media = await Media.getRelatedMedia(
data.results.map((result) => result.id)
);
return res.status(200).json({
page: data.page,
totalPages: data.total_pages,
totalResults: data.total_results,
genre,
results: data.results.map((result) =>
mapMovieResult(
result,
media.find(
(req) =>
req.tmdbId === result.id && req.mediaType === MediaType.MOVIE
)
)
),
});
}
);
discoverRoutes.get<{ studioId: string }>(
'/movies/studio/:studioId',
async (req, res) => {
const tmdb = createTmdbWithRegionLanaguage(req.user);
const studio = await tmdb.getStudio(Number(req.params.studioId));
const data = await tmdb.getDiscoverMovies({
page: Number(req.query.page),
language: req.query.language as string,
studio: Number(req.params.studioId),
});
const media = await Media.getRelatedMedia(
data.results.map((result) => result.id)
);
return res.status(200).json({
page: data.page,
totalPages: data.total_pages,
totalResults: data.total_results,
studio: mapProductionCompany(studio),
results: data.results.map((result) =>
mapMovieResult(
result,
media.find(
(req) =>
req.tmdbId === result.id && req.mediaType === MediaType.MOVIE
)
)
),
});
}
);
discoverRoutes.get('/movies/upcoming', async (req, res) => {
const tmdb = createTmdbWithRegionLanaguage(req.user);
@@ -124,6 +202,80 @@ discoverRoutes.get('/tv', async (req, res) => {
});
});
discoverRoutes.get<{ genreId: string }>(
'/tv/genre/:genreId',
async (req, res) => {
const tmdb = createTmdbWithRegionLanaguage(req.user);
const genres = await tmdb.getTvGenres({
language: req.query.language as string,
});
const genre = genres.find(
(genre) => genre.id === Number(req.params.genreId)
);
const data = await tmdb.getDiscoverTv({
page: Number(req.query.page),
language: req.query.language as string,
genre: Number(req.params.genreId),
});
const media = await Media.getRelatedMedia(
data.results.map((result) => result.id)
);
return res.status(200).json({
page: data.page,
totalPages: data.total_pages,
totalResults: data.total_results,
genre,
results: data.results.map((result) =>
mapTvResult(
result,
media.find(
(med) => med.tmdbId === result.id && med.mediaType === MediaType.TV
)
)
),
});
}
);
discoverRoutes.get<{ networkId: string }>(
'/tv/network/:networkId',
async (req, res) => {
const tmdb = createTmdbWithRegionLanaguage(req.user);
const network = await tmdb.getNetwork(Number(req.params.networkId));
const data = await tmdb.getDiscoverTv({
page: Number(req.query.page),
language: req.query.language as string,
network: Number(req.params.networkId),
});
const media = await Media.getRelatedMedia(
data.results.map((result) => result.id)
);
return res.status(200).json({
page: data.page,
totalPages: data.total_pages,
totalResults: data.total_results,
network: mapNetwork(network),
results: data.results.map((result) =>
mapTvResult(
result,
media.find(
(med) => med.tmdbId === result.id && med.mediaType === MediaType.TV
)
)
),
});
}
);
discoverRoutes.get('/tv/upcoming', async (req, res) => {
const tmdb = createTmdbWithRegionLanaguage(req.user);

View File

@@ -17,6 +17,8 @@ import { getAppVersion, getCommitTag } from '../utils/appVersion';
import serviceRoutes from './service';
import { appDataStatus, appDataPath } from '../utils/appDataVolume';
import TheMovieDb from '../api/themoviedb';
import { mapProductionCompany } from '../models/Movie';
import { mapNetwork } from '../models/Tv';
const router = Router();
@@ -79,7 +81,7 @@ router.get<{ id: string }>('/studio/:id', async (req, res) => {
const studio = await tmdb.getStudio(Number(req.params.id));
return res.status(200).json(studio);
return res.status(200).json(mapProductionCompany(studio));
});
router.get<{ id: string }>('/network/:id', async (req, res) => {
@@ -87,7 +89,7 @@ router.get<{ id: string }>('/network/:id', async (req, res) => {
const network = await tmdb.getNetwork(Number(req.params.id));
return res.status(200).json(network);
return res.status(200).json(mapNetwork(network));
});
router.get('/genres/movie', isAuthenticated(), async (req, res) => {