zyachel 2023-04-15 21:02:10 +05:30
parent 18ca98fd4a
commit 75732e0086
21 changed files with 2150 additions and 2 deletions

View file

@ -107,7 +107,7 @@ Inspired by projects like [teddit](https://codeberg.org/teddit/teddit), [nitter]
- [ ] lists
- [ ] moviemeter
- [ ] person info(includes directors and actors)
- [x] person info(includes directors and actors)
- [ ] company info
- [ ] user info

View 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;

View 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;

View 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;

View 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;

View 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>
);

View 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;

View 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';

File diff suppressed because it is too large Load diff

View 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'];

View 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;

View 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);
}

View 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);
}
}

View file

@ -0,0 +1,4 @@
.bio {
display: grid;
gap: var(--comp-whitespace);
}

View 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);
}
}
}

View 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);
}
}

View 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);
}
}

View 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
View 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;

View file

@ -101,7 +101,7 @@ const cleanTitle = (rawData: RawTitle) => {
total: misc.videos.total,
videos: misc.videoStrip.edges.map(video => ({
id: video.node.id,
type: video.node.contentType.displayName,
type: video.node.contentType.displayName.value,
caption: video.node.name.value,
runtime: video.node.runtime.value,
thumbnail: video.node.thumbnail.url,

View 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;