feat: add caching for TVDB images (#1655)

This commit is contained in:
Gauthier
2025-05-12 10:50:24 +02:00
committed by GitHub
parent d226dbb9b4
commit e69649d71d
3 changed files with 35 additions and 10 deletions

View File

@@ -3,20 +3,36 @@ import logger from '@server/logger';
import { Router } from 'express'; import { Router } from 'express';
const router = Router(); const router = Router();
const tmdbImageProxy = new ImageProxy('tmdb', 'https://image.tmdb.org', { const tmdbImageProxy = new ImageProxy('tmdb', 'https://image.tmdb.org', {
rateLimitOptions: { rateLimitOptions: {
maxRequests: 20, maxRequests: 20,
maxRPS: 50, maxRPS: 50,
}, },
}); });
const tvdbImageProxy = new ImageProxy('tvdb', 'https://artworks.thetvdb.com', {
rateLimitOptions: {
maxRequests: 20,
maxRPS: 50,
},
});
/** router.get('/:type/*', async (req, res) => {
* Image Proxy const imagePath = req.path.replace(/^\/\w+/, '');
*/
router.get('/*', async (req, res) => {
const imagePath = req.path.replace('/image', '');
try { try {
const imageData = await tmdbImageProxy.getImage(imagePath); let imageData;
if (req.params.type === 'tmdb') {
imageData = await tmdbImageProxy.getImage(imagePath);
} else if (req.params.type === 'tvdb') {
imageData = await tvdbImageProxy.getImage(imagePath);
} else {
logger.error('Unsupported image type', {
imagePath,
type: req.params.type,
});
res.status(400).send('Unsupported image type');
return;
}
res.writeHead(200, { res.writeHead(200, {
'Content-Type': `image/${imageData.meta.extension}`, 'Content-Type': `image/${imageData.meta.extension}`,

View File

@@ -6,7 +6,7 @@ const imageLoader: ImageLoader = ({ src }) => src;
export type CachedImageProps = ImageProps & { export type CachedImageProps = ImageProps & {
src: string; src: string;
type: 'tmdb' | 'avatar'; type: 'tmdb' | 'avatar' | 'tvdb';
}; };
/** /**
@@ -22,7 +22,15 @@ const CachedImage = ({ src, type, ...props }: CachedImageProps) => {
// tmdb stuff // tmdb stuff
imageUrl = imageUrl =
currentSettings.cacheImages && !src.startsWith('/') currentSettings.cacheImages && !src.startsWith('/')
? src.replace(/^https:\/\/image\.tmdb\.org\//, '/imageproxy/') ? src.replace(/^https:\/\/image\.tmdb\.org\//, '/imageproxy/tmdb/')
: src;
} else if (type === 'tvdb') {
imageUrl =
currentSettings.cacheImages && !src.startsWith('/')
? src.replace(
/^https:\/\/artworks\.thetvdb\.com\//,
'/imageproxy/tvdb/'
)
: src; : src;
} else if (type === 'avatar') { } else if (type === 'avatar') {
// jellyfin avatar (if any) // jellyfin avatar (if any)

View File

@@ -1,9 +1,9 @@
import Alert from '@app/components/Common/Alert'; import Alert from '@app/components/Common/Alert';
import CachedImage from '@app/components/Common/CachedImage';
import Modal from '@app/components/Common/Modal'; import Modal from '@app/components/Common/Modal';
import globalMessages from '@app/i18n/globalMessages'; import globalMessages from '@app/i18n/globalMessages';
import defineMessages from '@app/utils/defineMessages'; import defineMessages from '@app/utils/defineMessages';
import type { SonarrSeries } from '@server/api/servarr/sonarr'; import type { SonarrSeries } from '@server/api/servarr/sonarr';
import Image from 'next/image';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import useSWR from 'swr'; import useSWR from 'swr';
@@ -89,7 +89,8 @@ const SearchByNameModal = ({
} `} } `}
> >
<div className="relative flex w-24 flex-none items-center space-x-4 self-stretch"> <div className="relative flex w-24 flex-none items-center space-x-4 self-stretch">
<Image <CachedImage
type="tvdb"
src={ src={
item.remotePoster ?? item.remotePoster ??
'/images/jellyseerr_poster_not_found.png' '/images/jellyseerr_poster_not_found.png'