mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
feat: add language-filtered Discover pages (#1111)
This commit is contained in:
@@ -3332,6 +3332,53 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/MovieResult'
|
$ref: '#/components/schemas/MovieResult'
|
||||||
|
/discover/movies/language/{language}:
|
||||||
|
get:
|
||||||
|
summary: Discover movies by original language
|
||||||
|
description: Returns a list of movies based on the provided ISO 639-1 language code in a JSON object.
|
||||||
|
tags:
|
||||||
|
- search
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: language
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: en
|
||||||
|
- in: query
|
||||||
|
name: page
|
||||||
|
schema:
|
||||||
|
type: number
|
||||||
|
example: 1
|
||||||
|
default: 1
|
||||||
|
- in: query
|
||||||
|
name: language
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: en
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Results
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
page:
|
||||||
|
type: number
|
||||||
|
example: 1
|
||||||
|
totalPages:
|
||||||
|
type: number
|
||||||
|
example: 20
|
||||||
|
totalResults:
|
||||||
|
type: number
|
||||||
|
example: 200
|
||||||
|
language:
|
||||||
|
$ref: '#/components/schemas/SpokenLanguage'
|
||||||
|
results:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/MovieResult'
|
||||||
/discover/movies/studio/{studioId}:
|
/discover/movies/studio/{studioId}:
|
||||||
get:
|
get:
|
||||||
summary: Discover movies by studio
|
summary: Discover movies by studio
|
||||||
@@ -3467,6 +3514,53 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/TvResult'
|
$ref: '#/components/schemas/TvResult'
|
||||||
|
/discover/tv/language/{language}:
|
||||||
|
get:
|
||||||
|
summary: Discover TV shows by original language
|
||||||
|
description: Returns a list of TV shows based on the provided ISO 639-1 language code in a JSON object.
|
||||||
|
tags:
|
||||||
|
- search
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: language
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: en
|
||||||
|
- in: query
|
||||||
|
name: page
|
||||||
|
schema:
|
||||||
|
type: number
|
||||||
|
example: 1
|
||||||
|
default: 1
|
||||||
|
- in: query
|
||||||
|
name: language
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: en
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Results
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
page:
|
||||||
|
type: number
|
||||||
|
example: 1
|
||||||
|
totalPages:
|
||||||
|
type: number
|
||||||
|
example: 20
|
||||||
|
totalResults:
|
||||||
|
type: number
|
||||||
|
example: 200
|
||||||
|
language:
|
||||||
|
$ref: '#/components/schemas/SpokenLanguage'
|
||||||
|
results:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/TvResult'
|
||||||
/discover/tv/genre/{genreId}:
|
/discover/tv/genre/{genreId}:
|
||||||
get:
|
get:
|
||||||
summary: Discover TV shows by genre
|
summary: Discover TV shows by genre
|
||||||
|
@@ -34,6 +34,7 @@ interface DiscoverMovieOptions {
|
|||||||
language?: string;
|
language?: string;
|
||||||
primaryReleaseDateGte?: string;
|
primaryReleaseDateGte?: string;
|
||||||
primaryReleaseDateLte?: string;
|
primaryReleaseDateLte?: string;
|
||||||
|
originalLanguage?: string;
|
||||||
genre?: number;
|
genre?: number;
|
||||||
studio?: number;
|
studio?: number;
|
||||||
sortBy?:
|
sortBy?:
|
||||||
@@ -59,6 +60,7 @@ interface DiscoverTvOptions {
|
|||||||
firstAirDateGte?: string;
|
firstAirDateGte?: string;
|
||||||
firstAirDateLte?: string;
|
firstAirDateLte?: string;
|
||||||
includeEmptyReleaseDate?: boolean;
|
includeEmptyReleaseDate?: boolean;
|
||||||
|
originalLanguage?: string;
|
||||||
genre?: number;
|
genre?: number;
|
||||||
network?: number;
|
network?: number;
|
||||||
sortBy?:
|
sortBy?:
|
||||||
@@ -368,6 +370,7 @@ class TheMovieDb extends ExternalAPI {
|
|||||||
language = 'en',
|
language = 'en',
|
||||||
primaryReleaseDateGte,
|
primaryReleaseDateGte,
|
||||||
primaryReleaseDateLte,
|
primaryReleaseDateLte,
|
||||||
|
originalLanguage,
|
||||||
genre,
|
genre,
|
||||||
studio,
|
studio,
|
||||||
}: DiscoverMovieOptions = {}): Promise<TmdbSearchMovieResponse> => {
|
}: DiscoverMovieOptions = {}): Promise<TmdbSearchMovieResponse> => {
|
||||||
@@ -379,7 +382,7 @@ class TheMovieDb extends ExternalAPI {
|
|||||||
include_adult: includeAdult,
|
include_adult: includeAdult,
|
||||||
language,
|
language,
|
||||||
region: this.region,
|
region: this.region,
|
||||||
with_original_language: this.originalLanguage,
|
with_original_language: originalLanguage ?? this.originalLanguage,
|
||||||
'primary_release_date.gte': primaryReleaseDateGte,
|
'primary_release_date.gte': primaryReleaseDateGte,
|
||||||
'primary_release_date.lte': primaryReleaseDateLte,
|
'primary_release_date.lte': primaryReleaseDateLte,
|
||||||
with_genres: genre,
|
with_genres: genre,
|
||||||
@@ -400,6 +403,7 @@ class TheMovieDb extends ExternalAPI {
|
|||||||
firstAirDateGte,
|
firstAirDateGte,
|
||||||
firstAirDateLte,
|
firstAirDateLte,
|
||||||
includeEmptyReleaseDate = false,
|
includeEmptyReleaseDate = false,
|
||||||
|
originalLanguage,
|
||||||
genre,
|
genre,
|
||||||
network,
|
network,
|
||||||
}: DiscoverTvOptions = {}): Promise<TmdbSearchTvResponse> => {
|
}: DiscoverTvOptions = {}): Promise<TmdbSearchTvResponse> => {
|
||||||
@@ -412,7 +416,7 @@ class TheMovieDb extends ExternalAPI {
|
|||||||
region: this.region,
|
region: this.region,
|
||||||
'first_air_date.gte': firstAirDateGte,
|
'first_air_date.gte': firstAirDateGte,
|
||||||
'first_air_date.lte': firstAirDateLte,
|
'first_air_date.lte': firstAirDateLte,
|
||||||
with_original_language: this.originalLanguage,
|
with_original_language: originalLanguage ?? this.originalLanguage,
|
||||||
include_null_first_air_dates: includeEmptyReleaseDate,
|
include_null_first_air_dates: includeEmptyReleaseDate,
|
||||||
with_genres: genre,
|
with_genres: genre,
|
||||||
with_networks: network,
|
with_networks: network,
|
||||||
|
@@ -63,9 +63,52 @@ discoverRoutes.get('/movies', async (req, res) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
discoverRoutes.get<{ language: string }>(
|
||||||
|
'/movies/language/:language',
|
||||||
|
async (req, res, next) => {
|
||||||
|
const tmdb = createTmdbWithRegionLanaguage(req.user);
|
||||||
|
|
||||||
|
const languages = await tmdb.getLanguages();
|
||||||
|
|
||||||
|
const language = languages.find(
|
||||||
|
(lang) => lang.iso_639_1 === req.params.language
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!language) {
|
||||||
|
return next({ status: 404, message: 'Unable to retrieve language' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await tmdb.getDiscoverMovies({
|
||||||
|
page: Number(req.query.page),
|
||||||
|
language: req.query.language as string,
|
||||||
|
originalLanguage: req.params.language,
|
||||||
|
});
|
||||||
|
|
||||||
|
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,
|
||||||
|
language,
|
||||||
|
results: data.results.map((result) =>
|
||||||
|
mapMovieResult(
|
||||||
|
result,
|
||||||
|
media.find(
|
||||||
|
(req) =>
|
||||||
|
req.tmdbId === result.id && req.mediaType === MediaType.MOVIE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
discoverRoutes.get<{ genreId: string }>(
|
discoverRoutes.get<{ genreId: string }>(
|
||||||
'/movies/genre/:genreId',
|
'/movies/genre/:genreId',
|
||||||
async (req, res) => {
|
async (req, res, next) => {
|
||||||
const tmdb = createTmdbWithRegionLanaguage(req.user);
|
const tmdb = createTmdbWithRegionLanaguage(req.user);
|
||||||
|
|
||||||
const genres = await tmdb.getMovieGenres({
|
const genres = await tmdb.getMovieGenres({
|
||||||
@@ -76,6 +119,10 @@ discoverRoutes.get<{ genreId: string }>(
|
|||||||
(genre) => genre.id === Number(req.params.genreId)
|
(genre) => genre.id === Number(req.params.genreId)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!genre) {
|
||||||
|
return next({ status: 404, message: 'Unable to retrieve genre' });
|
||||||
|
}
|
||||||
|
|
||||||
const data = await tmdb.getDiscoverMovies({
|
const data = await tmdb.getDiscoverMovies({
|
||||||
page: Number(req.query.page),
|
page: Number(req.query.page),
|
||||||
language: req.query.language as string,
|
language: req.query.language as string,
|
||||||
@@ -106,36 +153,40 @@ discoverRoutes.get<{ genreId: string }>(
|
|||||||
|
|
||||||
discoverRoutes.get<{ studioId: string }>(
|
discoverRoutes.get<{ studioId: string }>(
|
||||||
'/movies/studio/:studioId',
|
'/movies/studio/:studioId',
|
||||||
async (req, res) => {
|
async (req, res, next) => {
|
||||||
const tmdb = new TheMovieDb();
|
const tmdb = new TheMovieDb();
|
||||||
|
|
||||||
const studio = await tmdb.getStudio(Number(req.params.studioId));
|
try {
|
||||||
|
const studio = await tmdb.getStudio(Number(req.params.studioId));
|
||||||
|
|
||||||
const data = await tmdb.getDiscoverMovies({
|
const data = await tmdb.getDiscoverMovies({
|
||||||
page: Number(req.query.page),
|
page: Number(req.query.page),
|
||||||
language: req.query.language as string,
|
language: req.query.language as string,
|
||||||
studio: Number(req.params.studioId),
|
studio: Number(req.params.studioId),
|
||||||
});
|
});
|
||||||
|
|
||||||
const media = await Media.getRelatedMedia(
|
const media = await Media.getRelatedMedia(
|
||||||
data.results.map((result) => result.id)
|
data.results.map((result) => result.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
page: data.page,
|
page: data.page,
|
||||||
totalPages: data.total_pages,
|
totalPages: data.total_pages,
|
||||||
totalResults: data.total_results,
|
totalResults: data.total_results,
|
||||||
studio: mapProductionCompany(studio),
|
studio: mapProductionCompany(studio),
|
||||||
results: data.results.map((result) =>
|
results: data.results.map((result) =>
|
||||||
mapMovieResult(
|
mapMovieResult(
|
||||||
result,
|
result,
|
||||||
media.find(
|
media.find(
|
||||||
(req) =>
|
(med) =>
|
||||||
req.tmdbId === result.id && req.mediaType === MediaType.MOVIE
|
med.tmdbId === result.id && med.mediaType === MediaType.MOVIE
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
),
|
});
|
||||||
});
|
} catch (e) {
|
||||||
|
return next({ status: 404, message: 'Unable to retrieve studio' });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -202,9 +253,51 @@ discoverRoutes.get('/tv', async (req, res) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
discoverRoutes.get<{ language: string }>(
|
||||||
|
'/tv/language/:language',
|
||||||
|
async (req, res, next) => {
|
||||||
|
const tmdb = createTmdbWithRegionLanaguage(req.user);
|
||||||
|
|
||||||
|
const languages = await tmdb.getLanguages();
|
||||||
|
|
||||||
|
const language = languages.find(
|
||||||
|
(lang) => lang.iso_639_1 === req.params.language
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!language) {
|
||||||
|
return next({ status: 404, message: 'Unable to retrieve language' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await tmdb.getDiscoverTv({
|
||||||
|
page: Number(req.query.page),
|
||||||
|
language: req.query.language as string,
|
||||||
|
originalLanguage: req.params.language,
|
||||||
|
});
|
||||||
|
|
||||||
|
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,
|
||||||
|
language,
|
||||||
|
results: data.results.map((result) =>
|
||||||
|
mapTvResult(
|
||||||
|
result,
|
||||||
|
media.find(
|
||||||
|
(med) => med.tmdbId === result.id && med.mediaType === MediaType.TV
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
discoverRoutes.get<{ genreId: string }>(
|
discoverRoutes.get<{ genreId: string }>(
|
||||||
'/tv/genre/:genreId',
|
'/tv/genre/:genreId',
|
||||||
async (req, res) => {
|
async (req, res, next) => {
|
||||||
const tmdb = createTmdbWithRegionLanaguage(req.user);
|
const tmdb = createTmdbWithRegionLanaguage(req.user);
|
||||||
|
|
||||||
const genres = await tmdb.getTvGenres({
|
const genres = await tmdb.getTvGenres({
|
||||||
@@ -215,6 +308,10 @@ discoverRoutes.get<{ genreId: string }>(
|
|||||||
(genre) => genre.id === Number(req.params.genreId)
|
(genre) => genre.id === Number(req.params.genreId)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!genre) {
|
||||||
|
return next({ status: 404, message: 'Unable to retrieve genre' });
|
||||||
|
}
|
||||||
|
|
||||||
const data = await tmdb.getDiscoverTv({
|
const data = await tmdb.getDiscoverTv({
|
||||||
page: Number(req.query.page),
|
page: Number(req.query.page),
|
||||||
language: req.query.language as string,
|
language: req.query.language as string,
|
||||||
@@ -244,35 +341,40 @@ discoverRoutes.get<{ genreId: string }>(
|
|||||||
|
|
||||||
discoverRoutes.get<{ networkId: string }>(
|
discoverRoutes.get<{ networkId: string }>(
|
||||||
'/tv/network/:networkId',
|
'/tv/network/:networkId',
|
||||||
async (req, res) => {
|
async (req, res, next) => {
|
||||||
const tmdb = new TheMovieDb();
|
const tmdb = new TheMovieDb();
|
||||||
|
|
||||||
const network = await tmdb.getNetwork(Number(req.params.networkId));
|
try {
|
||||||
|
const network = await tmdb.getNetwork(Number(req.params.networkId));
|
||||||
|
|
||||||
const data = await tmdb.getDiscoverTv({
|
const data = await tmdb.getDiscoverTv({
|
||||||
page: Number(req.query.page),
|
page: Number(req.query.page),
|
||||||
language: req.query.language as string,
|
language: req.query.language as string,
|
||||||
network: Number(req.params.networkId),
|
network: Number(req.params.networkId),
|
||||||
});
|
});
|
||||||
|
|
||||||
const media = await Media.getRelatedMedia(
|
const media = await Media.getRelatedMedia(
|
||||||
data.results.map((result) => result.id)
|
data.results.map((result) => result.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
page: data.page,
|
page: data.page,
|
||||||
totalPages: data.total_pages,
|
totalPages: data.total_pages,
|
||||||
totalResults: data.total_results,
|
totalResults: data.total_results,
|
||||||
network: mapNetwork(network),
|
network: mapNetwork(network),
|
||||||
results: data.results.map((result) =>
|
results: data.results.map((result) =>
|
||||||
mapTvResult(
|
mapTvResult(
|
||||||
result,
|
result,
|
||||||
media.find(
|
media.find(
|
||||||
(med) => med.tmdbId === result.id && med.mediaType === MediaType.TV
|
(med) =>
|
||||||
|
med.tmdbId === result.id && med.mediaType === MediaType.TV
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
),
|
});
|
||||||
});
|
} catch (e) {
|
||||||
|
return next({ status: 404, message: 'Unable to retrieve network' });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -331,15 +433,18 @@ discoverRoutes.get('/trending', async (req, res) => {
|
|||||||
? mapMovieResult(
|
? mapMovieResult(
|
||||||
result,
|
result,
|
||||||
media.find(
|
media.find(
|
||||||
(req) =>
|
(med) =>
|
||||||
req.tmdbId === result.id && req.mediaType === MediaType.MOVIE
|
med.tmdbId === result.id && med.mediaType === MediaType.MOVIE
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
: isPerson(result)
|
: isPerson(result)
|
||||||
? mapPersonResult(result)
|
? mapPersonResult(result)
|
||||||
: mapTvResult(
|
: mapTvResult(
|
||||||
result,
|
result,
|
||||||
media.find((req) => req.tmdbId === result.id && MediaType.TV)
|
media.find(
|
||||||
|
(med) =>
|
||||||
|
med.tmdbId === result.id && med.mediaType === MediaType.TV
|
||||||
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
@@ -368,8 +473,8 @@ discoverRoutes.get<{ keywordId: string }>(
|
|||||||
mapMovieResult(
|
mapMovieResult(
|
||||||
result,
|
result,
|
||||||
media.find(
|
media.find(
|
||||||
(req) =>
|
(med) =>
|
||||||
req.tmdbId === result.id && req.mediaType === MediaType.MOVIE
|
med.tmdbId === result.id && med.mediaType === MediaType.MOVIE
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
71
src/components/Discover/DiscoverMovieLanguage/index.tsx
Normal file
71
src/components/Discover/DiscoverMovieLanguage/index.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { MovieResult } from '../../../../server/models/Search';
|
||||||
|
import ListView from '../../Common/ListView';
|
||||||
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
import Header from '../../Common/Header';
|
||||||
|
import PageTitle from '../../Common/PageTitle';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import globalMessages from '../../../i18n/globalMessages';
|
||||||
|
import useDiscover from '../../../hooks/useDiscover';
|
||||||
|
import Error from '../../../pages/_error';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
languageMovies: '{language} Movies',
|
||||||
|
});
|
||||||
|
|
||||||
|
const DiscoverMovieLanguage: React.FC = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const {
|
||||||
|
isLoadingInitialData,
|
||||||
|
isEmpty,
|
||||||
|
isLoadingMore,
|
||||||
|
isReachingEnd,
|
||||||
|
titles,
|
||||||
|
fetchMore,
|
||||||
|
error,
|
||||||
|
} = useDiscover<
|
||||||
|
MovieResult,
|
||||||
|
{
|
||||||
|
originalLanguage: {
|
||||||
|
iso_639_1: string;
|
||||||
|
english_name: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
>(`/api/v1/discover/movies/language/${router.query.language}`);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <Error statusCode={500} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = isLoadingInitialData
|
||||||
|
? intl.formatMessage(globalMessages.loading)
|
||||||
|
: intl.formatMessage(messages.languageMovies, {
|
||||||
|
language: intl.formatDisplayName(router.query.language as string, {
|
||||||
|
type: 'language',
|
||||||
|
fallback: 'none',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PageTitle title={title} />
|
||||||
|
<div className="mt-1 mb-5">
|
||||||
|
<Header>{title}</Header>
|
||||||
|
</div>
|
||||||
|
<ListView
|
||||||
|
items={titles}
|
||||||
|
isEmpty={isEmpty}
|
||||||
|
isLoading={
|
||||||
|
isLoadingInitialData || (isLoadingMore && (titles?.length ?? 0) > 0)
|
||||||
|
}
|
||||||
|
isReachingEnd={isReachingEnd}
|
||||||
|
onScrollBottom={fetchMore}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DiscoverMovieLanguage;
|
71
src/components/Discover/DiscoverTvLanguage/index.tsx
Normal file
71
src/components/Discover/DiscoverTvLanguage/index.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { TvResult } from '../../../../server/models/Search';
|
||||||
|
import ListView from '../../Common/ListView';
|
||||||
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
import Header from '../../Common/Header';
|
||||||
|
import PageTitle from '../../Common/PageTitle';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import globalMessages from '../../../i18n/globalMessages';
|
||||||
|
import useDiscover from '../../../hooks/useDiscover';
|
||||||
|
import Error from '../../../pages/_error';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
languageSeries: '{language} Series',
|
||||||
|
});
|
||||||
|
|
||||||
|
const DiscoverTvLanguage: React.FC = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const {
|
||||||
|
isLoadingInitialData,
|
||||||
|
isEmpty,
|
||||||
|
isLoadingMore,
|
||||||
|
isReachingEnd,
|
||||||
|
titles,
|
||||||
|
fetchMore,
|
||||||
|
error,
|
||||||
|
} = useDiscover<
|
||||||
|
TvResult,
|
||||||
|
{
|
||||||
|
originalLanguage: {
|
||||||
|
iso_639_1: string;
|
||||||
|
english_name: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
>(`/api/v1/discover/tv/language/${router.query.language}`);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <Error statusCode={500} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = isLoadingInitialData
|
||||||
|
? intl.formatMessage(globalMessages.loading)
|
||||||
|
: intl.formatMessage(messages.languageSeries, {
|
||||||
|
language: intl.formatDisplayName(router.query.language as string, {
|
||||||
|
type: 'language',
|
||||||
|
fallback: 'none',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PageTitle title={title} />
|
||||||
|
<div className="mt-1 mb-5">
|
||||||
|
<Header>{title}</Header>
|
||||||
|
</div>
|
||||||
|
<ListView
|
||||||
|
items={titles}
|
||||||
|
isEmpty={isEmpty}
|
||||||
|
isLoading={
|
||||||
|
isLoadingInitialData || (isLoadingMore && (titles?.length ?? 0) > 0)
|
||||||
|
}
|
||||||
|
isReachingEnd={isReachingEnd}
|
||||||
|
onScrollBottom={fetchMore}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DiscoverTvLanguage;
|
@@ -651,19 +651,23 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{data.spokenLanguages.some(
|
{data.originalLanguage && (
|
||||||
(lng) => lng.iso_639_1 === data.originalLanguage
|
|
||||||
) && (
|
|
||||||
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
{intl.formatMessage(messages.originallanguage)}
|
{intl.formatMessage(messages.originallanguage)}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex-1 text-sm text-right text-gray-400">
|
<span className="flex-1 text-sm text-right text-gray-400">
|
||||||
{
|
<Link href={`/discover/tv/language/${data.originalLanguage}`}>
|
||||||
data.spokenLanguages.find(
|
<a className="hover:underline">
|
||||||
(lng) => lng.iso_639_1 === data.originalLanguage
|
{intl.formatDisplayName(data.originalLanguage, {
|
||||||
)?.name
|
type: 'language',
|
||||||
}
|
fallback: 'none',
|
||||||
|
}) ??
|
||||||
|
data.spokenLanguages.find(
|
||||||
|
(lng) => lng.iso_639_1 === data.originalLanguage
|
||||||
|
)?.name}
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@@ -675,19 +675,23 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
{data.status}
|
{data.status}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{data.spokenLanguages.some(
|
{data.originalLanguage && (
|
||||||
(lng) => lng.iso_639_1 === data.originalLanguage
|
|
||||||
) && (
|
|
||||||
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
<div className="flex px-4 py-2 border-b border-gray-800 last:border-b-0">
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
{intl.formatMessage(messages.originallanguage)}
|
{intl.formatMessage(messages.originallanguage)}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex-1 text-sm text-right text-gray-400">
|
<span className="flex-1 text-sm text-right text-gray-400">
|
||||||
{
|
<Link href={`/discover/tv/language/${data.originalLanguage}`}>
|
||||||
data.spokenLanguages.find(
|
<a className="hover:underline">
|
||||||
(lng) => lng.iso_639_1 === data.originalLanguage
|
{intl.formatDisplayName(data.originalLanguage, {
|
||||||
)?.name
|
type: 'language',
|
||||||
}
|
fallback: 'none',
|
||||||
|
}) ??
|
||||||
|
data.spokenLanguages.find(
|
||||||
|
(lng) => lng.iso_639_1 === data.originalLanguage
|
||||||
|
)?.name}
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@@ -15,9 +15,11 @@
|
|||||||
"components.CollectionDetails.requestswillbecreated4k": "The following titles will have 4K requests created for them:",
|
"components.CollectionDetails.requestswillbecreated4k": "The following titles will have 4K requests created for them:",
|
||||||
"components.Common.ListView.noresults": "No results.",
|
"components.Common.ListView.noresults": "No results.",
|
||||||
"components.Discover.DiscoverMovieGenre.genreMovies": "{genre} Movies",
|
"components.Discover.DiscoverMovieGenre.genreMovies": "{genre} Movies",
|
||||||
|
"components.Discover.DiscoverMovieLanguage.languageMovies": "{language} Movies",
|
||||||
"components.Discover.DiscoverNetwork.networkSeries": "{network} Series",
|
"components.Discover.DiscoverNetwork.networkSeries": "{network} Series",
|
||||||
"components.Discover.DiscoverStudio.studioMovies": "{studio} Movies",
|
"components.Discover.DiscoverStudio.studioMovies": "{studio} Movies",
|
||||||
"components.Discover.DiscoverTvGenre.genreSeries": "{genre} Series",
|
"components.Discover.DiscoverTvGenre.genreSeries": "{genre} Series",
|
||||||
|
"components.Discover.DiscoverTvLanguage.languageSeries": "{language} Series",
|
||||||
"components.Discover.NetworkSlider.networks": "Networks",
|
"components.Discover.NetworkSlider.networks": "Networks",
|
||||||
"components.Discover.StudioSlider.studios": "Studios",
|
"components.Discover.StudioSlider.studios": "Studios",
|
||||||
"components.Discover.discover": "Discover",
|
"components.Discover.discover": "Discover",
|
||||||
|
9
src/pages/discover/movies/language/[language]/index.tsx
Normal file
9
src/pages/discover/movies/language/[language]/index.tsx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { NextPage } from 'next';
|
||||||
|
import DiscoverMovieLanguage from '../../../../../components/Discover/DiscoverMovieLanguage';
|
||||||
|
|
||||||
|
const DiscoverMovieLanguagePage: NextPage = () => {
|
||||||
|
return <DiscoverMovieLanguage />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DiscoverMovieLanguagePage;
|
9
src/pages/discover/tv/language/[language]/index.tsx
Normal file
9
src/pages/discover/tv/language/[language]/index.tsx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { NextPage } from 'next';
|
||||||
|
import DiscoverTvLanguage from '../../../../../components/Discover/DiscoverTvLanguage';
|
||||||
|
|
||||||
|
const DiscoverTvLanguagePage: NextPage = () => {
|
||||||
|
return <DiscoverTvLanguage />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DiscoverTvLanguagePage;
|
Reference in New Issue
Block a user