feat(route): add name route
adds much needed route fix https://github.com/zyachel/libremdb/issues/39, https://github.com/zyachel/libremdb/issues/36, https://codeberg.org/zyachel/libremdb/issues/11
This commit is contained in:
parent
18ca98fd4a
commit
75732e0086
@ -107,7 +107,7 @@ Inspired by projects like [teddit](https://codeberg.org/teddit/teddit), [nitter]
|
|||||||
|
|
||||||
- [ ] lists
|
- [ ] lists
|
||||||
- [ ] moviemeter
|
- [ ] moviemeter
|
||||||
- [ ] person info(includes directors and actors)
|
- [x] person info(includes directors and actors)
|
||||||
- [ ] company info
|
- [ ] company info
|
||||||
- [ ] user info
|
- [ ] user info
|
||||||
|
|
||||||
|
57
src/components/name/Basic.tsx
Normal file
57
src/components/name/Basic.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { CardBasic } from 'src/components/card';
|
||||||
|
import { Basic as BasicType } from 'src/interfaces/shared/name';
|
||||||
|
import { formatNumber } from 'src/utils/helpers';
|
||||||
|
import styles from 'src/styles/modules/components/name/basic.module.scss';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
className: string;
|
||||||
|
data: BasicType;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Basic = ({ data, className }: Props) => {
|
||||||
|
return (
|
||||||
|
<CardBasic className={className} image={data.poster.url} title={data.name}>
|
||||||
|
<div className={styles.ratings}>
|
||||||
|
{data.ranking && (
|
||||||
|
<p className={styles.rating}>
|
||||||
|
<span className={styles.rating__num}>{formatNumber(data.ranking.position)}</span>
|
||||||
|
<svg className={styles.rating__icon}>
|
||||||
|
<use href='/svg/sprite.svg#icon-graph-rising'></use>
|
||||||
|
</svg>
|
||||||
|
<span className={styles.rating__text}>
|
||||||
|
{' '}
|
||||||
|
Popularity (
|
||||||
|
<span className={styles.rating__sub}>{getRankingStats(data.ranking)}</span>)
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!!data.primaryProfessions.length && (
|
||||||
|
<p className={styles.genres}>
|
||||||
|
<span className={styles.heading}>Profession: </span>
|
||||||
|
{data.primaryProfessions.join(', ')}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{
|
||||||
|
<p className={styles.overview}>
|
||||||
|
<span className={styles.heading}>About: </span>
|
||||||
|
{data.bio.short}...
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
<p className={styles.genres}>
|
||||||
|
<span className={styles.heading}>Known for: </span>
|
||||||
|
{data.knownFor.title} ({data.knownFor.role})
|
||||||
|
</p>
|
||||||
|
</CardBasic>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRankingStats = (ranking: NonNullable<Props['data']['ranking']>) => {
|
||||||
|
if (ranking.direction === 'FLAT') return '\u2192';
|
||||||
|
|
||||||
|
const change = formatNumber(ranking.change);
|
||||||
|
return (ranking.direction === 'UP' ? '\u2191' : '\u2193') + change;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Basic;
|
12
src/components/name/Bio.tsx
Normal file
12
src/components/name/Bio.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import styles from 'src/styles/modules/components/name/did-you-know.module.scss';
|
||||||
|
|
||||||
|
type Props = { bio: string };
|
||||||
|
|
||||||
|
const Bio = ({ bio }: Props) => (
|
||||||
|
<section className={styles.bio}>
|
||||||
|
<h2 className='heading heading__secondary'>About</h2>
|
||||||
|
<div dangerouslySetInnerHTML={{ __html: bio }} />
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Bio;
|
73
src/components/name/Credits.tsx
Normal file
73
src/components/name/Credits.tsx
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { Credits } from 'src/interfaces/shared/name';
|
||||||
|
import { CardTitle } from 'src/components/card';
|
||||||
|
import styles from 'src/styles/modules/components/name/credits.module.scss';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
className: string;
|
||||||
|
data: Credits;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Credits = ({ className, data }: Props) => {
|
||||||
|
if (!data.total) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={`${className} ${styles.credits}`}>
|
||||||
|
<h2 className='heading heading__secondary'>Credits</h2>
|
||||||
|
<section>
|
||||||
|
<h3 className='heading heading__tertiary'>Released</h3>
|
||||||
|
{data.released.map(
|
||||||
|
(item, i) =>
|
||||||
|
!!item.total && (
|
||||||
|
<details open={i === 0} key={item.category.id}>
|
||||||
|
<summary>
|
||||||
|
{item.category.text} ({item.total})
|
||||||
|
</summary>
|
||||||
|
<ul className={styles.container} key={item.category.id}>
|
||||||
|
{item.titles.map(title => (
|
||||||
|
<CardTitle
|
||||||
|
key={title.id}
|
||||||
|
link={`/title/${title.id}`}
|
||||||
|
name={title.title}
|
||||||
|
titleType={title.type.text}
|
||||||
|
image={title.poster?.url}
|
||||||
|
year={title.releaseYear}
|
||||||
|
ratings={title.ratings}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h3 className='heading heading__tertiary'>Unreleased</h3>
|
||||||
|
{data.unreleased.map(
|
||||||
|
(item, i) =>
|
||||||
|
!!item.total && (
|
||||||
|
<details open={i === 0} key={item.category.id}>
|
||||||
|
<summary>
|
||||||
|
{item.category.text} ({item.total})
|
||||||
|
</summary>
|
||||||
|
<ul className={styles.container}>
|
||||||
|
{item.titles.map(title => (
|
||||||
|
<CardTitle
|
||||||
|
key={title.id}
|
||||||
|
link={`/title/${title.id}`}
|
||||||
|
name={title.title}
|
||||||
|
titleType={title.type.text}
|
||||||
|
image={title.poster?.url}
|
||||||
|
year={title.releaseYear}
|
||||||
|
>
|
||||||
|
<p>{title.productionStatus}</p>
|
||||||
|
</CardTitle>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Credits;
|
53
src/components/name/DidYouKnow.tsx
Normal file
53
src/components/name/DidYouKnow.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import Link from 'next/link';
|
||||||
|
import { DidYouKnow } from 'src/interfaces/shared/name';
|
||||||
|
import styles from 'src/styles/modules/components/name/did-you-know.module.scss';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
data: DidYouKnow;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DidYouKnow = ({ data }: Props) => (
|
||||||
|
<section className={styles.didYouKnow}>
|
||||||
|
<h2 className='heading heading__secondary'>Did you know</h2>
|
||||||
|
<div className={styles.container}>
|
||||||
|
{!!data.trivia?.total && (
|
||||||
|
<section>
|
||||||
|
<h3 className='heading heading__tertiary'>Trivia</h3>
|
||||||
|
<div dangerouslySetInnerHTML={{ __html: data.trivia.html }}></div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
{!!data.quotes?.total && (
|
||||||
|
<section>
|
||||||
|
<h3 className='heading heading__tertiary'>Quotes</h3>
|
||||||
|
<div dangerouslySetInnerHTML={{ __html: data.quotes.html }}></div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
{!!data.trademark?.total && (
|
||||||
|
<section>
|
||||||
|
<h3 className='heading heading__tertiary'>Trademark</h3>
|
||||||
|
<div dangerouslySetInnerHTML={{ __html: data.trademark.html }}></div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
{!!data.nicknames.length && (
|
||||||
|
<section>
|
||||||
|
<h3 className='heading heading__tertiary'>Nicknames</h3>
|
||||||
|
<p>{data.nicknames.join(', ')}</p>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
{!!data.salary?.total && (
|
||||||
|
<section>
|
||||||
|
<h3 className='heading heading__tertiary'>Salary</h3>
|
||||||
|
<p>
|
||||||
|
<span>{data.salary.value} in </span>
|
||||||
|
<Link href={`/title/${data.salary.title.id}`}>
|
||||||
|
<a className={'link'}>{data.salary.title.text}</a>
|
||||||
|
</Link>
|
||||||
|
<span> ({data.salary.title.year})</span>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default DidYouKnow;
|
192
src/components/name/Info.tsx
Normal file
192
src/components/name/Info.tsx
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
import Link from 'next/link';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import Name, { PersonalDetails } from 'src/interfaces/shared/name';
|
||||||
|
import styles from 'src/styles/modules/components/name/info.module.scss';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
info: PersonalDetails;
|
||||||
|
accolades: Name['accolades'];
|
||||||
|
};
|
||||||
|
|
||||||
|
const PersonalDetails = ({ info, accolades }: Props) => {
|
||||||
|
const {
|
||||||
|
query: { nameId },
|
||||||
|
} = useRouter();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.info}>
|
||||||
|
<section className={styles.accolades}>
|
||||||
|
<h2 className='heading heading__secondary'>Accolades</h2>
|
||||||
|
<div className={styles.accolades__container}>
|
||||||
|
{accolades.awards && (
|
||||||
|
<p>
|
||||||
|
<span>
|
||||||
|
Won {accolades.awards.wins} {accolades.awards.name}
|
||||||
|
</span>
|
||||||
|
<span> (out of {accolades.awards.nominations} nominations)</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<p>
|
||||||
|
{accolades.wins} wins and {accolades.nominations} nominations in total
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<Link href={`/name/${nameId}/awards`}>
|
||||||
|
<a className='link'>View all awards</a>
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className={styles.details}>
|
||||||
|
<h2 className='heading heading__secondary'>Personal details</h2>
|
||||||
|
<div className={styles.details__container}>
|
||||||
|
{!!info.officialSites.length && (
|
||||||
|
<p>
|
||||||
|
<span>Official sites: </span>
|
||||||
|
{info.officialSites.map((site, i) => (
|
||||||
|
<span key={site.url}>
|
||||||
|
{!!i && ', '}
|
||||||
|
<a href={site.url} className='link' target='_blank' rel='noreferrer'>
|
||||||
|
{site.name}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{!!info.alsoKnownAs.length && (
|
||||||
|
<p>
|
||||||
|
<span>Also known as: </span>
|
||||||
|
<span>{info.alsoKnownAs.join(', ')}</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{info.height && (
|
||||||
|
<p>
|
||||||
|
<span>Height: </span>
|
||||||
|
<span>{info.height}</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{info.birth && (
|
||||||
|
<p>
|
||||||
|
<span>Born: </span>
|
||||||
|
<span>{info.birth.date}</span>
|
||||||
|
<span>
|
||||||
|
{' '}
|
||||||
|
in{' '}
|
||||||
|
<Link href={`/search/name?birth_place=${info.birth.location}`}>
|
||||||
|
<a className='link'>{info.birth.location}</a>
|
||||||
|
</Link>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{info.death.date && (
|
||||||
|
<p>
|
||||||
|
<span>Died: </span>
|
||||||
|
<span>{info.death.date}</span>
|
||||||
|
<span>
|
||||||
|
{' '}
|
||||||
|
in{' '}
|
||||||
|
<Link href={`/search/name?death_place=${info.death.location}`}>
|
||||||
|
<a className='link'>{info.death.location}</a>
|
||||||
|
</Link>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{info.death.cause && (
|
||||||
|
<p>
|
||||||
|
<span>Death cause: </span>
|
||||||
|
<span>{info.death.cause}</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{!!info.spouses?.length && (
|
||||||
|
<p>
|
||||||
|
<span>Spouses: </span>
|
||||||
|
{info.spouses.map((spouse, i) => (
|
||||||
|
<span key={spouse.name}>
|
||||||
|
<>
|
||||||
|
{!!i && ', '}
|
||||||
|
{renderPersonNameWithLink(spouse)} {spouse.range} (
|
||||||
|
{spouse.attributes.join(', ')})
|
||||||
|
</>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{!!info.children?.length && (
|
||||||
|
<p>
|
||||||
|
<span>Children: </span>
|
||||||
|
{info.children.map((child, i) => (
|
||||||
|
<span key={child.name}>
|
||||||
|
<>
|
||||||
|
{!!i && ', '}
|
||||||
|
{renderPersonNameWithLink(child)}
|
||||||
|
</>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{!!info.parents?.length && (
|
||||||
|
<p>
|
||||||
|
<span>Parents: </span>
|
||||||
|
{info.parents.map((parent, i) => (
|
||||||
|
<span key={parent.name}>
|
||||||
|
<>
|
||||||
|
{!!i && ', '}
|
||||||
|
{renderPersonNameWithLink(parent)}
|
||||||
|
</>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{!!info.relatives?.length && (
|
||||||
|
<p>
|
||||||
|
<span>Relatives: </span>
|
||||||
|
{info.relatives.map((relative, i) => (
|
||||||
|
<span key={relative.name}>
|
||||||
|
<>
|
||||||
|
{!!i && ', '}
|
||||||
|
{renderPersonNameWithLink(relative)} ({relative.relation})
|
||||||
|
</>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{!!info.otherWorks?.length && (
|
||||||
|
<p>
|
||||||
|
<span>Other Works: </span>
|
||||||
|
{info.otherWorks.map((work, i) => (
|
||||||
|
<span key={work.text}>
|
||||||
|
<>
|
||||||
|
{!!i && ', '}
|
||||||
|
<span dangerouslySetInnerHTML={{ __html: work.text }} />
|
||||||
|
</>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{!!info.publicity.total && (
|
||||||
|
<p>
|
||||||
|
<span>Publicity Listings: </span>
|
||||||
|
<span>{info.publicity.articles} Articles</span>,{' '}
|
||||||
|
<span>{info.publicity.interviews} Interviews</span>,{' '}
|
||||||
|
<span>{info.publicity.magazines} Magazines</span>,{' '}
|
||||||
|
<span>{info.publicity.pictorials} Pictorials</span>,{' '}
|
||||||
|
<span>{info.publicity.printBiographies} Print biographies</span>, and{' '}
|
||||||
|
<span>{info.publicity.filmBiographies} Biographies</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PersonalDetails;
|
||||||
|
|
||||||
|
const renderPersonNameWithLink = (person: { name: string; id: string | null }) =>
|
||||||
|
person.id ? (
|
||||||
|
<Link href={`/name/${person.id}`}>
|
||||||
|
<a className='link'>{person.name}</a>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span>{person.name}</span>
|
||||||
|
);
|
34
src/components/name/KnownFor.tsx
Normal file
34
src/components/name/KnownFor.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import type { KnownFor as KnownForType } from 'src/interfaces/shared/name';
|
||||||
|
import { CardTitle } from 'src/components/card';
|
||||||
|
import styles from 'src/styles/modules/components/name/known-for.module.scss';
|
||||||
|
|
||||||
|
type Props = { data: KnownForType };
|
||||||
|
|
||||||
|
const KnownFor = ({ data }: Props) => {
|
||||||
|
if (!data.length) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={styles.knownFor}>
|
||||||
|
<h2 className='heading heading__secondary'>Known For</h2>
|
||||||
|
<ul className={styles.container}>
|
||||||
|
{data.map(title => (
|
||||||
|
<CardTitle
|
||||||
|
key={title.id}
|
||||||
|
link={`/title/${title.id}`}
|
||||||
|
name={title.title}
|
||||||
|
titleType={title.type.text}
|
||||||
|
image={title.poster?.url}
|
||||||
|
year={title.releaseYear}
|
||||||
|
>
|
||||||
|
<p className={styles.item__role}>{getRoles(title)}</p>
|
||||||
|
</CardTitle>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRoles = (title: Props['data'][number]) =>
|
||||||
|
(title.summary.characters ?? title.summary.jobs)?.join(', ');
|
||||||
|
|
||||||
|
export default KnownFor;
|
6
src/components/name/index.tsx
Normal file
6
src/components/name/index.tsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export { default as Basic } from './Basic';
|
||||||
|
export { default as DidYouKnow } from './DidYouKnow';
|
||||||
|
export { default as Info } from './Info';
|
||||||
|
export { default as Credits } from './Credits';
|
||||||
|
export { default as KnownFor } from './KnownFor';
|
||||||
|
export { default as Bio } from './Bio';
|
1084
src/interfaces/misc/rawName.ts
Normal file
1084
src/interfaces/misc/rawName.ts
Normal file
File diff suppressed because it is too large
Load Diff
16
src/interfaces/shared/name.ts
Normal file
16
src/interfaces/shared/name.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import cleanName from 'src/utils/cleaners/name';
|
||||||
|
|
||||||
|
type Name = ReturnType<typeof cleanName>;
|
||||||
|
export type { Name as default };
|
||||||
|
|
||||||
|
export type Basic = Name['basic'];
|
||||||
|
|
||||||
|
export type Media = Name['media'];
|
||||||
|
|
||||||
|
export type Credits = Name['credits'];
|
||||||
|
|
||||||
|
export type DidYouKnow = Name['didYouKnow'];
|
||||||
|
|
||||||
|
export type PersonalDetails = Name['personalDetails'];
|
||||||
|
|
||||||
|
export type KnownFor = Name['knownFor'];
|
62
src/pages/name/[nameId]/index.tsx
Normal file
62
src/pages/name/[nameId]/index.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { GetServerSideProps, InferGetServerSidePropsType } from 'next';
|
||||||
|
import Meta from 'src/components/meta/Meta';
|
||||||
|
import Layout from 'src/layouts/Layout';
|
||||||
|
import ErrorInfo from 'src/components/error/ErrorInfo';
|
||||||
|
import Media from 'src/components/media/Media';
|
||||||
|
import { Basic, Credits, DidYouKnow, Info, Bio, KnownFor } from 'src/components/name';
|
||||||
|
import Name from 'src/interfaces/shared/name';
|
||||||
|
import { AppError } from 'src/interfaces/shared/error';
|
||||||
|
import name from 'src/utils/fetchers/name';
|
||||||
|
import { getProxiedIMDbImgUrl } from 'src/utils/helpers';
|
||||||
|
import styles from 'src/styles/modules/pages/name/name.module.scss';
|
||||||
|
|
||||||
|
type Props = InferGetServerSidePropsType<typeof getServerSideProps>;
|
||||||
|
|
||||||
|
const NameInfo = ({ data, error }: Props) => {
|
||||||
|
if (error) return <ErrorInfo message={error.message} statusCode={error.statusCode} />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Meta
|
||||||
|
title={data.basic.name}
|
||||||
|
description={data.basic.bio.short + '...'}
|
||||||
|
imgUrl={data.basic.poster?.url && getProxiedIMDbImgUrl(data.basic.poster.url)}
|
||||||
|
/>
|
||||||
|
<Layout className={styles.name}>
|
||||||
|
<Basic data={data.basic} className={styles.basic} />
|
||||||
|
<Media className={styles.media} media={data.media} />
|
||||||
|
<div className={styles.textarea}>
|
||||||
|
<KnownFor data={data.knownFor} />
|
||||||
|
<Bio bio={data.basic.bio.full} />
|
||||||
|
</div>
|
||||||
|
<div className={styles.infoarea}>
|
||||||
|
<Info info={data.personalDetails} accolades={data.accolades} />
|
||||||
|
<DidYouKnow data={data.didYouKnow} />
|
||||||
|
</div>
|
||||||
|
<Credits className={styles.credits} data={data.credits} />
|
||||||
|
</Layout>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type Data = { data: Name; error: null } | { error: AppError; data: null };
|
||||||
|
type Params = { nameId: string };
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps<Data, Params> = async ctx => {
|
||||||
|
const nameId = ctx.params!.nameId;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await name(nameId);
|
||||||
|
|
||||||
|
return { props: { data, error: null } };
|
||||||
|
} catch (error: any) {
|
||||||
|
const { message, statusCode } = error;
|
||||||
|
|
||||||
|
ctx.res.statusCode = statusCode;
|
||||||
|
ctx.res.statusMessage = message;
|
||||||
|
|
||||||
|
return { props: { error: { message, statusCode }, data: null } };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NameInfo;
|
54
src/styles/modules/components/name/basic.module.scss
Normal file
54
src/styles/modules/components/name/basic.module.scss
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
@use '../../../abstracts' as helper;
|
||||||
|
|
||||||
|
.ratings {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--spacer-0) var(--spacer-3);
|
||||||
|
|
||||||
|
@include helper.bp('bp-900') {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating {
|
||||||
|
font-size: var(--fs-5);
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, max-content);
|
||||||
|
place-items: center;
|
||||||
|
gap: 0 var(--spacer-0);
|
||||||
|
|
||||||
|
&__num {
|
||||||
|
grid-column: 1 / 2;
|
||||||
|
font-size: 1.8em;
|
||||||
|
font-weight: var(--fw-medium);
|
||||||
|
// line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
--dim: 1.8em;
|
||||||
|
grid-column: -2 / -1;
|
||||||
|
line-height: 1;
|
||||||
|
height: var(--dim);
|
||||||
|
width: var(--dim);
|
||||||
|
display: grid;
|
||||||
|
place-content: center;
|
||||||
|
fill: var(--clr-fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
font-size: 0.9em;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
color: var(--clr-text-muted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
@include helper.prettify-link(var(--clr-link));
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading {
|
||||||
|
font-weight: var(--fw-bold);
|
||||||
|
}
|
49
src/styles/modules/components/name/credits.module.scss
Normal file
49
src/styles/modules/components/name/credits.module.scss
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
@use '../../../abstracts' as helper;
|
||||||
|
|
||||||
|
.credits {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--comp-whitespace);
|
||||||
|
|
||||||
|
& > section {
|
||||||
|
overflow-x: auto;
|
||||||
|
display: grid;
|
||||||
|
gap: var(--spacer-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
details {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: var(--fs-4);
|
||||||
|
color: var(--clr-text-accent);
|
||||||
|
font-family: var(--ff-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
--max-width: 18rem;
|
||||||
|
--min-height: 40rem;
|
||||||
|
|
||||||
|
list-style: none;
|
||||||
|
overflow-x: auto;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
// grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: var(--spacer-4);
|
||||||
|
padding: var(--spacer-1) var(--spacer-2) var(--spacer-3) var(--spacer-2);
|
||||||
|
|
||||||
|
grid-auto-columns: var(--max-width);
|
||||||
|
min-height: var(--min-height);
|
||||||
|
|
||||||
|
> li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include helper.bp('bp-700') {
|
||||||
|
grid-auto-columns: calc(var(--max-width) - 1rem);
|
||||||
|
min-height: calc(var(--min-height) - 5rem);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
.bio {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--comp-whitespace);
|
||||||
|
}
|
21
src/styles/modules/components/name/info.module.scss
Normal file
21
src/styles/modules/components/name/info.module.scss
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
.info {
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
gap: var(--doc-whitespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
.accolades, .details {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--comp-whitespace);
|
||||||
|
|
||||||
|
&__container {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--spacer-0);
|
||||||
|
|
||||||
|
// for span elements like these: 'release date:'
|
||||||
|
& > p > span:first-of-type {
|
||||||
|
font-weight: var(--fw-bold);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
32
src/styles/modules/components/name/known-for.module.scss
Normal file
32
src/styles/modules/components/name/known-for.module.scss
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
@use '../../../abstracts' as helper;
|
||||||
|
|
||||||
|
.knownFor {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--comp-whitespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
--max-width: 18rem;
|
||||||
|
--min-height: 40rem;
|
||||||
|
|
||||||
|
list-style: none;
|
||||||
|
overflow-x: auto;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
// grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: var(--spacer-4);
|
||||||
|
padding: 0 var(--spacer-2) var(--spacer-3) var(--spacer-2);
|
||||||
|
|
||||||
|
grid-auto-columns: var(--max-width);
|
||||||
|
min-height: var(--min-height);
|
||||||
|
|
||||||
|
> li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include helper.bp('bp-700') {
|
||||||
|
grid-auto-columns: calc(var(--max-width) - 1rem);
|
||||||
|
min-height: calc(var(--min-height) - 5rem);
|
||||||
|
}
|
||||||
|
}
|
26
src/styles/modules/components/name/reviews.module.scss
Normal file
26
src/styles/modules/components/name/reviews.module.scss
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
.reviews {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--comp-whitespace);
|
||||||
|
|
||||||
|
&__reviewContainer {
|
||||||
|
// background-color: antiquewhite;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__stats {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--spacer-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.review {
|
||||||
|
&__summary {
|
||||||
|
font-size: calc(var(--fs-5) * 1.1);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text,
|
||||||
|
&__metadata {
|
||||||
|
padding-top: var(--spacer-2);
|
||||||
|
}
|
||||||
|
}
|
64
src/styles/modules/pages/name/name.module.scss
Normal file
64
src/styles/modules/pages/name/name.module.scss
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
@use '../../../abstracts' as helper;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
--doc-whitespace: var(--spacer-8);
|
||||||
|
--comp-whitespace: var(--spacer-3);
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
gap: var(--doc-whitespace);
|
||||||
|
padding: var(--doc-whitespace);
|
||||||
|
align-items: start;
|
||||||
|
|
||||||
|
grid-template-columns: repeat(8, 1fr);
|
||||||
|
grid-template-areas:
|
||||||
|
'basic basic basic basic basic basic basic basic'
|
||||||
|
'media media media media media media media media'
|
||||||
|
'text text text text text info info info'
|
||||||
|
'credits credits credits credits credits credits credits credits';
|
||||||
|
|
||||||
|
@include helper.bp('bp-1200') {
|
||||||
|
grid-template-columns: none;
|
||||||
|
grid-template-areas:
|
||||||
|
'basic'
|
||||||
|
'media'
|
||||||
|
'known'
|
||||||
|
'text'
|
||||||
|
'info'
|
||||||
|
'credits';
|
||||||
|
}
|
||||||
|
|
||||||
|
@include helper.bp('bp-700') {
|
||||||
|
--doc-whitespace: var(--spacer-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@include helper.bp('bp-450') {
|
||||||
|
padding: var(--spacer-3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.basic {
|
||||||
|
grid-area: basic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media {
|
||||||
|
grid-area: media;
|
||||||
|
}
|
||||||
|
|
||||||
|
.credits {
|
||||||
|
grid-area: credits;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textarea {
|
||||||
|
grid-area: text;
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
gap: var(--doc-whitespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoarea {
|
||||||
|
grid-area: info;
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
gap: var(--doc-whitespace);
|
||||||
|
}
|
281
src/utils/cleaners/name.ts
Normal file
281
src/utils/cleaners/name.ts
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
import RawName from 'src/interfaces/misc/rawName';
|
||||||
|
|
||||||
|
const cleanName = (rawData: RawName) => {
|
||||||
|
const {
|
||||||
|
props: {
|
||||||
|
pageProps: { aboveTheFold: main, mainColumnData: misc },
|
||||||
|
},
|
||||||
|
} = rawData;
|
||||||
|
|
||||||
|
const cleanData = {
|
||||||
|
nameId: main.id,
|
||||||
|
basic: {
|
||||||
|
id: main.id,
|
||||||
|
name: main.nameText.text,
|
||||||
|
nameSuffix: main.disambiguator?.text ?? null,
|
||||||
|
knownFor: {
|
||||||
|
title: main.knownFor.edges[0].node.title.titleText.text,
|
||||||
|
role: main.knownFor.edges[0].node.summary.principalCategory.text,
|
||||||
|
},
|
||||||
|
...(main.primaryImage && {
|
||||||
|
poster: {
|
||||||
|
url: main.primaryImage.url,
|
||||||
|
id: main.primaryImage.id,
|
||||||
|
caption: main.primaryImage.caption.plainText,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
primaryProfessions: main.primaryProfessions.map(profession => profession.category.text),
|
||||||
|
bio: {
|
||||||
|
full: main.bio.text.plaidHtml,
|
||||||
|
short: main.bio.text.plainText.slice(0, 600),
|
||||||
|
},
|
||||||
|
birthDate: main.birthDate?.displayableProperty.value.plainText ?? null,
|
||||||
|
deathStatus: main.deathStatus,
|
||||||
|
deathDate: main.deathDate?.displayableProperty.value.plainText ?? null,
|
||||||
|
...(main.meterRanking && {
|
||||||
|
ranking: {
|
||||||
|
position: main.meterRanking.currentRank,
|
||||||
|
change: main.meterRanking.rankChange.difference,
|
||||||
|
direction: main.meterRanking.rankChange.changeDirection,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
media: {
|
||||||
|
...(main.primaryVideos.edges.length && {
|
||||||
|
trailer: {
|
||||||
|
id: main.primaryVideos.edges[0].node.id,
|
||||||
|
isMature: main.primaryVideos.edges[0].node.isMature,
|
||||||
|
thumbnail: main.primaryVideos.edges[0].node.thumbnail.url,
|
||||||
|
runtime: main.primaryVideos.edges[0].node.runtime.value,
|
||||||
|
caption: main.primaryVideos.edges[0].node.description?.value ?? null,
|
||||||
|
urls: main.primaryVideos.edges[0].node.playbackURLs.map(url => ({
|
||||||
|
resolution: url.displayName.value,
|
||||||
|
mimeType: url.mimeType,
|
||||||
|
url: url.url,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
images: {
|
||||||
|
total: misc.images.total,
|
||||||
|
images: misc.images.edges.map(image => ({
|
||||||
|
id: image.node.id,
|
||||||
|
url: image.node.url,
|
||||||
|
caption: image.node.caption,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
videos: {
|
||||||
|
total: misc.videos.total,
|
||||||
|
videos: misc.videos.edges.map(video => ({
|
||||||
|
id: video.node.id,
|
||||||
|
type: video.node.contentType.displayName.value,
|
||||||
|
caption: video.node.name.value,
|
||||||
|
runtime: video.node.runtime.value,
|
||||||
|
thumbnail: video.node.thumbnail.url,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
accolades: {
|
||||||
|
wins: misc.wins.total,
|
||||||
|
nominations: misc.nominations.total,
|
||||||
|
...(misc.prestigiousAwardSummary && {
|
||||||
|
awards: {
|
||||||
|
name: misc.prestigiousAwardSummary.award.text,
|
||||||
|
id: misc.prestigiousAwardSummary.award.id,
|
||||||
|
event: misc.prestigiousAwardSummary.award.event.id,
|
||||||
|
nominations: misc.prestigiousAwardSummary.nominations,
|
||||||
|
wins: misc.prestigiousAwardSummary.wins,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
knownFor: misc.knownFor.edges.map(item => ({
|
||||||
|
id: item.node.title.id,
|
||||||
|
title: item.node.title.titleText.text,
|
||||||
|
...(item.node.title.primaryImage && {
|
||||||
|
poster: {
|
||||||
|
id: item.node.title.primaryImage.id,
|
||||||
|
url: item.node.title.primaryImage.url,
|
||||||
|
caption: item.node.title.primaryImage.caption.plainText,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
type: {
|
||||||
|
id: item.node.title.titleType.id,
|
||||||
|
text: item.node.title.titleType.text,
|
||||||
|
},
|
||||||
|
certificate: item.node.title.certificate?.rating ?? null,
|
||||||
|
...(item.node.title.releaseYear && {
|
||||||
|
releaseYear: {
|
||||||
|
start: item.node.title.releaseYear.year,
|
||||||
|
end: item.node.title.releaseYear.endYear ?? null,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
runtime: item.node.title.runtime?.seconds ?? null,
|
||||||
|
ratings: {
|
||||||
|
avg: item.node.title.ratingsSummary.aggregateRating ?? null,
|
||||||
|
numVotes: item.node.title.ratingsSummary.voteCount,
|
||||||
|
},
|
||||||
|
genres: item.node.title.titleGenres.genres.map(genre => genre.genre.text),
|
||||||
|
|
||||||
|
summary: {
|
||||||
|
numEpisodes: item.node.summary.episodeCount ?? null,
|
||||||
|
years: {
|
||||||
|
start: item.node.summary.yearRange.year,
|
||||||
|
end: item.node.summary.yearRange.endYear ?? null,
|
||||||
|
},
|
||||||
|
characters: item.node.summary.principalCharacters?.map(character => character.name) ?? null,
|
||||||
|
jobs: item.node.summary.principalJobs?.map(job => job.text) ?? null,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
credits: {
|
||||||
|
total: misc.totalCredits.total,
|
||||||
|
summary: {
|
||||||
|
titleType: misc.creditSummary.titleTypeCategories.map(cat => ({
|
||||||
|
total: cat.total,
|
||||||
|
id: cat.titleTypeCategory.id,
|
||||||
|
label: cat.titleTypeCategory.text,
|
||||||
|
})),
|
||||||
|
genres: misc.creditSummary.genres.map(genre => ({
|
||||||
|
total: genre.total,
|
||||||
|
name: genre.genre.displayableProperty.value.plainText,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
released: getCredits(misc.releasedPrimaryCredits),
|
||||||
|
unreleased: getCredits<'unreleased'>(misc.unreleasedPrimaryCredits),
|
||||||
|
},
|
||||||
|
personalDetails: {
|
||||||
|
officialSites: misc.personalDetailsExternalLinks.edges.map(item => ({
|
||||||
|
name: item.node.label,
|
||||||
|
url: item.node.url,
|
||||||
|
})),
|
||||||
|
alsoKnownAs: misc.akas.edges.map(item => item.node.displayableProperty.value.plainText),
|
||||||
|
height: misc.height?.displayableProperty.value.plainText ?? null,
|
||||||
|
birth: {
|
||||||
|
location: misc.birthLocation?.text ?? null,
|
||||||
|
date: main.birthDate?.displayableProperty.value.plainText ?? null,
|
||||||
|
},
|
||||||
|
death: {
|
||||||
|
location: misc.deathLocation?.displayableProperty.value.plainText ?? null,
|
||||||
|
cause: misc.deathCause?.displayableProperty.value.plainText ?? null,
|
||||||
|
date: main.deathDate?.displayableProperty.value.plainText ?? null,
|
||||||
|
},
|
||||||
|
spouses:
|
||||||
|
misc.personalDetailsSpouses?.map(spouse => ({
|
||||||
|
name: spouse.spouse.asMarkdown.plainText,
|
||||||
|
id: spouse.spouse.name?.id ?? null,
|
||||||
|
range: spouse.timeRange.displayableProperty.value.plaidHtml,
|
||||||
|
attributes: spouse.attributes.map(attr => attr.text),
|
||||||
|
})) ?? null,
|
||||||
|
children: misc.children.edges.map(child => ({
|
||||||
|
name: child.node.relationName.displayableProperty.value.plainText,
|
||||||
|
id: child.node.relationName.name?.id ?? null,
|
||||||
|
})),
|
||||||
|
parents: misc.parents.edges.map(parent => ({
|
||||||
|
name: parent.node.relationName.displayableProperty.value.plainText,
|
||||||
|
id: parent.node.relationName.name?.id ?? null,
|
||||||
|
})),
|
||||||
|
relatives: misc.others.edges.map(relative => ({
|
||||||
|
relation: relative.node.relationshipType.text,
|
||||||
|
id: relative.node.relationName.name?.id ?? null,
|
||||||
|
name: relative.node.relationName.displayableProperty.value.plainText,
|
||||||
|
})),
|
||||||
|
otherWorks: misc.otherWorks.edges.map(work => ({
|
||||||
|
summary: work.node.category?.text ?? null,
|
||||||
|
text: work.node.text.plaidHtml,
|
||||||
|
})),
|
||||||
|
publicity: {
|
||||||
|
total: misc.publicityListings.total,
|
||||||
|
filmBiographies: misc.nameFilmBiography.total,
|
||||||
|
printBiographies: misc.namePrintBiography.total,
|
||||||
|
interviews: misc.publicityInterview.total,
|
||||||
|
articles: misc.publicityArticle.total,
|
||||||
|
magazines: misc.publicityMagazineCover.total,
|
||||||
|
pictorials: misc.publicityPictorial.total,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
didYouKnow: {
|
||||||
|
...(misc.trivia.edges.length && {
|
||||||
|
trivia: {
|
||||||
|
total: misc.triviaTotal.total,
|
||||||
|
html: misc.trivia.edges[0].node.displayableArticle.body.plaidHtml,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
...(misc.trademarks.edges.length && {
|
||||||
|
trademark: {
|
||||||
|
total: misc.trademarksTotal.total,
|
||||||
|
html: misc.trademarks.edges[0].node.displayableArticle.body.plaidHtml,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
...(misc.quotes.edges.length && {
|
||||||
|
quotes: {
|
||||||
|
total: misc.quotesTotal.total,
|
||||||
|
html: misc.quotes.edges[0].node.displayableArticle.body.plaidHtml,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
nicknames: misc.nickNames.map(name => name.displayableProperty.value.plainText),
|
||||||
|
...(misc.titleSalaries.edges.length && {
|
||||||
|
salary: {
|
||||||
|
total: misc.titleSalariesTotal.total,
|
||||||
|
value: misc.titleSalaries.edges[0].node.displayableProperty.value.plainText,
|
||||||
|
title: {
|
||||||
|
id: misc.titleSalaries.edges[0].node.title.id,
|
||||||
|
year: misc.titleSalaries.edges[0].node.title.releaseYear?.year ?? null,
|
||||||
|
text: misc.titleSalaries.edges[0].node.title.titleText.text,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return cleanData;
|
||||||
|
};
|
||||||
|
|
||||||
|
type RawReleased = RawName['props']['pageProps']['mainColumnData']['releasedPrimaryCredits'];
|
||||||
|
type RawUnreleased = RawName['props']['pageProps']['mainColumnData']['unreleasedPrimaryCredits'];
|
||||||
|
const getCredits = <T extends 'released' | 'unreleased' = 'released'>(
|
||||||
|
credits: T extends 'released' ? RawReleased : RawUnreleased
|
||||||
|
) =>
|
||||||
|
credits.map(creditItem => ({
|
||||||
|
category: creditItem.category,
|
||||||
|
total: creditItem.credits.total,
|
||||||
|
titles: creditItem.credits.edges.map(item => ({
|
||||||
|
id: item.node.title.id,
|
||||||
|
title: item.node.title.titleText.text,
|
||||||
|
...(item.node.title.primaryImage && {
|
||||||
|
poster: {
|
||||||
|
id: item.node.title.primaryImage.id,
|
||||||
|
url: item.node.title.primaryImage.url,
|
||||||
|
caption: item.node.title.primaryImage.caption.plainText,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
type: {
|
||||||
|
id: item.node.title.titleType.id,
|
||||||
|
text: item.node.title.titleType.text,
|
||||||
|
},
|
||||||
|
certificate: item.node.title.certificate?.rating ?? null,
|
||||||
|
...(item.node.title.releaseYear && {
|
||||||
|
releaseYear: {
|
||||||
|
start: item.node.title.releaseYear.year,
|
||||||
|
end: item.node.title.releaseYear.endYear ?? null,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
runtime: item.node.title.runtime?.seconds ?? null,
|
||||||
|
ratings: {
|
||||||
|
avg: item.node.title.ratingsSummary.aggregateRating ?? null,
|
||||||
|
numVotes: item.node.title.ratingsSummary.voteCount,
|
||||||
|
},
|
||||||
|
test: JSON.stringify(item.node.title),
|
||||||
|
genres: item.node.title.titleGenres.genres.map(genre => genre.genre.text),
|
||||||
|
productionStatus: item.node.title.productionStatus.currentProductionStage.text,
|
||||||
|
|
||||||
|
summary: {
|
||||||
|
numEpisodes: item.node.episodeCredits.total,
|
||||||
|
years: {
|
||||||
|
start: item.node.episodeCredits.yearRange?.year ?? null,
|
||||||
|
end: item.node.episodeCredits.yearRange?.endYear ?? null,
|
||||||
|
},
|
||||||
|
characters: item.node.characters?.map(char => char.name) ?? null,
|
||||||
|
jobs: item.node.jobs?.map(job => job.text) ?? null,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default cleanName;
|
@ -101,7 +101,7 @@ const cleanTitle = (rawData: RawTitle) => {
|
|||||||
total: misc.videos.total,
|
total: misc.videos.total,
|
||||||
videos: misc.videoStrip.edges.map(video => ({
|
videos: misc.videoStrip.edges.map(video => ({
|
||||||
id: video.node.id,
|
id: video.node.id,
|
||||||
type: video.node.contentType.displayName,
|
type: video.node.contentType.displayName.value,
|
||||||
caption: video.node.name.value,
|
caption: video.node.name.value,
|
||||||
runtime: video.node.runtime.value,
|
runtime: video.node.runtime.value,
|
||||||
thumbnail: video.node.thumbnail.url,
|
thumbnail: video.node.thumbnail.url,
|
||||||
|
28
src/utils/fetchers/name.ts
Normal file
28
src/utils/fetchers/name.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import * as cheerio from 'cheerio';
|
||||||
|
import RawName from 'src/interfaces/misc/rawName';
|
||||||
|
import axiosInstance from 'src/utils/axiosInstance';
|
||||||
|
import cleanName from 'src/utils/cleaners/name';
|
||||||
|
import { AppError } from 'src/utils/helpers';
|
||||||
|
|
||||||
|
const name = async (nameId: string) => {
|
||||||
|
try {
|
||||||
|
// getting data
|
||||||
|
const res = await axiosInstance(`/name/${nameId}`);
|
||||||
|
const $ = cheerio.load(res.data);
|
||||||
|
const rawData = $('script#__NEXT_DATA__').text();
|
||||||
|
// cleaning it a bit
|
||||||
|
const parsedRawData: RawName = JSON.parse(rawData);
|
||||||
|
const cleanData = cleanName(parsedRawData);
|
||||||
|
// returning
|
||||||
|
return cleanData;
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err.response?.status === 404) throw new AppError('not found', 404, err.cause);
|
||||||
|
|
||||||
|
console.warn(err);
|
||||||
|
|
||||||
|
|
||||||
|
throw new AppError('something went wrong', 500, err.cause);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default name;
|
Loading…
x
Reference in New Issue
Block a user