feat: fetch images from media proxy on frontend

This commit is contained in:
httpjamesm 2022-10-31 17:37:36 -04:00
parent b7ee6863e5
commit dba2ba5aa4
5 changed files with 70 additions and 52 deletions

View File

@ -1,22 +1,27 @@
import { Fragment } from 'react';
import Image from 'next/future/image';
import Link from 'next/link';
import { Fragment } from 'react'
import Image from 'next/future/image'
import Link from 'next/link'
import { formatNumber, formatTime, modifyIMDbImg } from '../../utils/helpers';
import { Basic } from '../../interfaces/shared/title';
import styles from '../../styles/modules/components/title/basic.module.scss';
import {
formatNumber,
formatTime,
getProxiedIMDbImgUrl,
modifyIMDbImg,
} from '../../utils/helpers'
import { Basic } from '../../interfaces/shared/title'
import styles from '../../styles/modules/components/title/basic.module.scss'
type Props = {
className: string;
data: Basic;
};
className: string
data: Basic
}
const Basic = ({ data, className }: Props) => {
const titleType = data.type.id;
const titleType = data.type.id
const releaseTime =
titleType === 'tvSeries'
? `${data.releaseYear?.start}-${data.releaseYear?.end || 'present'}`
: data.releaseYear?.start;
: data.releaseYear?.start
return (
<section
@ -29,7 +34,8 @@ const Basic = ({ data, className }: Props) => {
className={styles.imageContainer}
style={{
backgroundImage:
data.poster && `url(${modifyIMDbImg(data.poster.url, 300)})`,
data.poster &&
`url(${getProxiedIMDbImgUrl(modifyIMDbImg(data.poster.url, 300))})`,
}}
>
{data.poster ? (
@ -39,11 +45,11 @@ const Basic = ({ data, className }: Props) => {
alt={data.poster.caption}
priority
fill
sizes='300px'
sizes="300px"
/>
) : (
<svg className={styles.image__NA}>
<use href='/svg/sprite.svg#icon-image-slash' />
<use href="/svg/sprite.svg#icon-image-slash" />
</svg>
)}
</div>
@ -51,7 +57,7 @@ const Basic = ({ data, className }: Props) => {
<h1 className={`${styles.title} heading heading__primary`}>
{data.title}
</h1>
<ul className={styles.meta} aria-label='quick facts'>
<ul className={styles.meta} aria-label="quick facts">
{data.status.id !== 'released' && (
<li className={styles.meta__text}>{data.status.text}</li>
)}
@ -72,7 +78,7 @@ const Basic = ({ data, className }: Props) => {
<p className={styles.rating}>
<span className={styles.rating__num}>{data.ratings.avg}</span>
<svg className={styles.rating__icon}>
<use href='/svg/sprite.svg#icon-rating'></use>
<use href="/svg/sprite.svg#icon-rating"></use>
</svg>
<span className={styles.rating__text}> Avg. rating</span>
</p>
@ -81,7 +87,7 @@ const Basic = ({ data, className }: Props) => {
{formatNumber(data.ratings.numVotes)}
</span>
<svg className={styles.rating__icon}>
<use href='/svg/sprite.svg#icon-like-dislike'></use>
<use href="/svg/sprite.svg#icon-like-dislike"></use>
</svg>
<span className={styles.rating__text}> No. of votes</span>
</p>
@ -93,7 +99,7 @@ const Basic = ({ data, className }: Props) => {
{formatNumber(data.ranking.position)}
</span>
<svg className={styles.rating__icon}>
<use href='/svg/sprite.svg#icon-graph-rising'></use>
<use href="/svg/sprite.svg#icon-graph-rising"></use>
</svg>
<span className={styles.rating__text}>
{' '}
@ -130,7 +136,7 @@ const Basic = ({ data, className }: Props) => {
<span className={styles.overview__text}>{data.plot || '-'}</span>
</p>
}
{data.primaryCrew.map(crewType => (
{data.primaryCrew.map((crewType) => (
<p className={styles.crewType} key={crewType.type.id}>
<span className={styles.crewType__heading}>
{`${crewType.type.category}: `}
@ -147,7 +153,7 @@ const Basic = ({ data, className }: Props) => {
))}
</div>
</section>
);
};
)
}
export default Basic;
export default Basic

View File

@ -1,37 +1,39 @@
import Image from 'next/future/image';
import Link from 'next/link';
import { NextRouter } from 'next/router';
import { Media } from '../../interfaces/shared/title';
import { modifyIMDbImg } from '../../utils/helpers';
import Image from 'next/future/image'
import Link from 'next/link'
import { NextRouter } from 'next/router'
import { Media } from '../../interfaces/shared/title'
import { getProxiedIMDbImgUrl, modifyIMDbImg } from '../../utils/helpers'
import styles from '../../styles/modules/components/title/media.module.scss';
import styles from '../../styles/modules/components/title/media.module.scss'
type Props = {
className: string;
media: Media;
router: NextRouter;
};
className: string
media: Media
router: NextRouter
}
const Media = ({ className, media, router }: Props) => {
return (
<div className={`${className} ${styles.media}`}>
{(media.trailer || !!media.videos.total) && (
<section className={styles.videos}>
<h2 className='heading heading__secondary'>Videos</h2>
<h2 className="heading heading__secondary">Videos</h2>
<div className={styles.videos__container}>
{media.trailer && (
<div key={router.asPath} className={styles.trailer}>
<video
aria-label='trailer video'
aria-label="trailer video"
// it's a relatively new tag. hence jsx-all1 complains
aria-description={media.trailer.caption}
controls
playsInline
poster={modifyIMDbImg(media.trailer.thumbnail)}
poster={getProxiedIMDbImgUrl(
modifyIMDbImg(media.trailer.thumbnail)
)}
className={styles.trailer__video}
>
{media.trailer.urls.map(source => (
{media.trailer.urls.map((source) => (
<source
key={source.url}
type={source.mimeType}
@ -44,15 +46,15 @@ const Media = ({ className, media, router }: Props) => {
)}
{!!media.videos.total &&
media.videos.videos.map(video => (
media.videos.videos.map((video) => (
<Link href={`/video/${video.id}`} key={video.id}>
<a className={styles.video}>
<Image
className={styles.video__img}
src={modifyIMDbImg(video.thumbnail)}
alt=''
alt=""
fill
sizes='400px'
sizes="400px"
/>
<p className={styles.video__caption}>
{video.caption} ({video.runtime}s)
@ -65,16 +67,16 @@ const Media = ({ className, media, router }: Props) => {
)}
{!!media.images.total && (
<section className={styles.images}>
<h2 className='heading heading__secondary'>Images</h2>
<h2 className="heading heading__secondary">Images</h2>
<div className={styles.images__container}>
{media.images.images.map(image => (
{media.images.images.map((image) => (
<figure key={image.id} className={styles.image}>
<Image
className={styles.image__img}
src={modifyIMDbImg(image.url)}
alt=''
alt=""
fill
sizes='400px'
sizes="400px"
/>
<figcaption className={styles.image__caption}>
{image.caption.plainText}
@ -85,6 +87,6 @@ const Media = ({ className, media, router }: Props) => {
</section>
)}
</div>
);
};
export default Media;
)
}
export default Media

View File

@ -14,7 +14,7 @@ export default async function handler(
if (!mediaUrl) {
res.status(400)
res.send(null)
res.end()
return
}
@ -24,7 +24,7 @@ export default async function handler(
mediaUrlParsed = new URL(mediaUrl)
} catch {
res.status(400)
res.send(null)
res.end()
return
}
@ -33,13 +33,13 @@ export default async function handler(
if (!mediaDomain.endsWith('media-amazon.com')) {
res.status(400)
res.send(null)
res.end()
return
}
if (mediaUrlParsed.protocol !== 'https:') {
res.status(400)
res.send(null)
res.end()
return
}
@ -54,7 +54,7 @@ export default async function handler(
if (!validExtension) {
res.status(400)
res.send(null)
res.end()
return
}
@ -79,6 +79,7 @@ export default async function handler(
if (!mediaRes.ok) {
res.status(mediaRes.status)
res.end()
return
}

View File

@ -20,6 +20,7 @@ import { AppError } from '../../../interfaces/shared/error'
// styles
import styles from '../../../styles/modules/pages/title/title.module.scss'
import Head from 'next/head'
import { getProxiedIMDbImgUrl } from '../../../utils/helpers'
type Props = { data: Title; error: null } | { error: AppError; data: null }
@ -50,7 +51,11 @@ const TitleInfo = ({ data, error }: Props) => {
<Head>
<meta
title="og:image"
content={data.basic.poster?.url || '/icon-512.png'}
content={
data.basic.poster?.url
? getProxiedIMDbImgUrl(data.basic.poster?.url)
: '/icon-512.png'
}
/>
</Head>
<Layout className={styles.title}>

View File

@ -53,6 +53,10 @@ export const modifyIMDbImg = (url: string, widthInPx = 600) => {
return url.replaceAll('.jpg', `UX${widthInPx}.jpg`);
};
export const getProxiedIMDbImgUrl = (url: string) => {
return `/api/media_proxy?url=${encodeURIComponent(url)}`;
}
export const AppError = class extends Error {
constructor(message: string, public statusCode: number, cause?: any) {
super(message, cause);