mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
fetch media type via node.js
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import nodeFetch from 'node-fetch';
|
||||
|
||||
import { httpLogger } from 'lib/api/logger';
|
||||
import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
|
||||
export default async function mediaTypeHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
httpLogger(req, res);
|
||||
|
||||
try {
|
||||
const url = getQueryParamString(req.query.url);
|
||||
const response = await nodeFetch(url, { method: 'HEAD' });
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
const contentType = response.headers.get('content-type');
|
||||
const mediaType = contentType?.startsWith('video') ? 'video' : 'image';
|
||||
res.status(200).json({ type: mediaType });
|
||||
} catch (error) {
|
||||
res.status(200).json({ type: undefined });
|
||||
}
|
||||
}
|
||||
Vendored
+1
@@ -15,6 +15,7 @@ declare module "nextjs-routes" {
|
||||
| DynamicRoute<"/address/[hash]/contract_verification", { "hash": string }>
|
||||
| DynamicRoute<"/address/[hash]", { "hash": string }>
|
||||
| StaticRoute<"/api/csrf">
|
||||
| StaticRoute<"/api/media-type">
|
||||
| StaticRoute<"/api/proxy">
|
||||
| StaticRoute<"/api-docs">
|
||||
| DynamicRoute<"/apps/[id]", { "id": string }>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { AspectRatio, chakra, Skeleton } from '@chakra-ui/react';
|
||||
import { route } from 'nextjs-routes';
|
||||
import React from 'react';
|
||||
|
||||
import NftImage from './NftImage';
|
||||
import NftVideo from './NftVideo';
|
||||
import type { MediaType } from './utils';
|
||||
import { getPreliminaryMediaType } from './utils';
|
||||
|
||||
interface Props {
|
||||
imageUrl: string | null;
|
||||
@@ -11,7 +14,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const NftMedia = ({ imageUrl, animationUrl, className }: Props) => {
|
||||
const [ type, setType ] = React.useState<'image' | 'video' | undefined>(!animationUrl ? 'image' : undefined);
|
||||
const [ type, setType ] = React.useState<MediaType | undefined>(!animationUrl ? 'image' : undefined);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!animationUrl) {
|
||||
@@ -20,10 +23,26 @@ const NftMedia = ({ imageUrl, animationUrl, className }: Props) => {
|
||||
|
||||
// media could be either gif or video
|
||||
// so we pre-fetch the resources in order to get its content type
|
||||
fetch(animationUrl, { method: 'HEAD' })
|
||||
.then((response) => {
|
||||
const contentType = response.headers.get('content-type');
|
||||
setType(contentType?.startsWith('video') ? 'video' : 'image');
|
||||
// have to do it via Node.js due to strict CSP for connect-src
|
||||
// but in order not to abuse our server firstly we check file url extension
|
||||
// and if it is valid we will trust it and display corresponding media component
|
||||
|
||||
const preliminaryType = getPreliminaryMediaType(animationUrl);
|
||||
|
||||
if (preliminaryType) {
|
||||
setType(preliminaryType);
|
||||
return;
|
||||
}
|
||||
|
||||
const url = route({ pathname: '/api/media-type', query: { url: animationUrl } });
|
||||
fetch(url)
|
||||
.then((response) => response.json())
|
||||
.then((_response) => {
|
||||
const response = _response as { type: MediaType | undefined };
|
||||
setType(response.type || 'image');
|
||||
})
|
||||
.catch(() => {
|
||||
setType('image');
|
||||
});
|
||||
|
||||
}, [ animationUrl ]);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
export type MediaType = 'image' | 'video';
|
||||
|
||||
const IMAGE_EXTENSIONS = [
|
||||
'.jpg', 'jpeg',
|
||||
'.png',
|
||||
'.gif',
|
||||
'.svg',
|
||||
];
|
||||
|
||||
const VIDEO_EXTENSIONS = [
|
||||
'.mp4',
|
||||
'.webm',
|
||||
'.ogg',
|
||||
];
|
||||
|
||||
export function getPreliminaryMediaType(url: string): MediaType | undefined {
|
||||
if (IMAGE_EXTENSIONS.some((ext) => url.endsWith(ext))) {
|
||||
return 'image';
|
||||
}
|
||||
|
||||
if (url.startsWith('data:image')) {
|
||||
return 'image';
|
||||
}
|
||||
|
||||
if (VIDEO_EXTENSIONS.some((ext) => url.endsWith(ext))) {
|
||||
return 'video';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user