Added images to the artists cards (Introduced a bug needing to be fixed (discover/musics not loading anymore))

pull/3800/merge^2
Anatole Sot 4 months ago
parent b8740cf22b
commit df198f1d2e

@ -678,6 +678,8 @@ components:
mediaType:
type: string
example: artist
posterPath:
type: string
title:
type: string
example: Album Name

@ -294,7 +294,10 @@ class MusicBrainz extends BaseNodeBrainz {
constructor() {
super({
userAgent:
'Overseer-with-lidar-support/0.0.1 ( https://github.com/ano0002/overseerr )',
'Overseer-with-lidar-support/0.1 ( https://github.com/ano0002/overseerr )',
retryOn: true,
retryDelay: 3000,
retryCount: 3,
});
}

@ -1,8 +1,47 @@
import type { mbRelease, mbReleaseGroup } from './interfaces';
import LidarrAPI from '@server/api/servarr/lidarr';
import { getSettings } from '@server/lib/settings';
import type { mbArtist, mbRelease, mbReleaseGroup } from './interfaces';
function getPosterFromMB(
element: mbRelease | mbReleaseGroup
element: mbRelease | mbReleaseGroup | mbArtist
): string | undefined {
if (element.media_type === 'artist') {
const settings = getSettings();
const lidarrSettings = settings.lidarr.find((lidarr) => lidarr.isDefault);
if (!lidarrSettings) {
throw new Error('No default Lidarr instance found');
}
const lidarr: LidarrAPI = new LidarrAPI({
apiKey: lidarrSettings.apiKey,
url: LidarrAPI.buildUrl(lidarrSettings, '/api/v1'),
});
const artist = (lidarr as LidarrAPI).getArtist(element.id);
return LidarrAPI.buildUrl(
lidarrSettings,
(artist.images ?? [{ coverType: 'poster', url: undefined }]).find(
(i) => i.coverType === 'poster'
)?.url
);
}
return `https://coverartarchive.org/${element.media_type}/${element.id}/front-250.jpg`;
}
async function getFanartFromMB(element: mbArtist): Promise<string | undefined> {
const settings = getSettings();
const lidarrSettings = settings.lidarr.find((lidarr) => lidarr.isDefault);
if (!lidarrSettings) {
throw new Error('No default Lidarr instance found');
}
const lidarr = new LidarrAPI({
apiKey: lidarrSettings.apiKey,
url: LidarrAPI.buildUrl(lidarrSettings, '/api/v1'),
});
const artist = await lidarr.getArtist(element.id);
return (
artist.images ?? [{ coverType: 'fanart', remoteUrl: undefined }]
).filter((i) => i.coverType === 'fanart')[0].remoteUrl;
}
export default getPosterFromMB;
export { getFanartFromMB };

@ -137,38 +137,80 @@ export interface Medium {
}
class LidarrAPI extends ServarrBase<{ musicId: number }> {
static lastArtistsUpdate = 0;
static artists: LidarrArtist[] = [];
static delay = 1000 * 60 * 5;
constructor({ url, apiKey }: { url: string; apiKey: string }) {
super({ url, apiKey, cacheName: 'lidarr', apiName: 'Lidarr' });
if (LidarrAPI.lastArtistsUpdate < Date.now() - LidarrAPI.delay) {
this.getArtists();
}
}
public getArtists = async (): Promise<LidarrArtist[]> => {
public getArtists = (): void => {
try {
LidarrAPI.lastArtistsUpdate = Date.now();
this.axios.get<LidarrArtist[]>('/artist').then((response) => {
LidarrAPI.artists = response.data;
});
} catch (e) {
throw new Error(`[Lidarr] Failed to retrieve artists: ${e.message}`);
}
};
public getArtist = (id: string | number): LidarrArtist => {
try {
const response = await this.axios.get<LidarrArtist[]>('/artist');
if (LidarrAPI.lastArtistsUpdate < Date.now() - LidarrAPI.delay) {
this.getArtists();
}
if (typeof id === 'number') {
const result = LidarrAPI.artists.find((artist) => artist.id === id);
if (result) {
return result;
}
throw new Error(`[Lidarr] Artist not found (using Lidarr Id): ${id}`);
}
const result = LidarrAPI.artists.find(
(artist) => artist.foreignArtistId === id
);
if (result) {
return result;
}
throw new Error(`[Lidarr] Artist not found (using MusicBrainzId): ${id}`);
} catch (e) {
throw new Error(`[Lidarr] Failed to retrieve artist: ${e.message}`);
}
};
public getAlbums = async (): Promise<LidarrAlbum[]> => {
try {
const response = await this.axios.get<LidarrAlbum[]>('/album');
return response.data;
} catch (e) {
throw new Error(`[Lidarr] Failed to retrieve musics: ${e.message}`);
throw new Error(`[Lidarr] Failed to retrieve albums: ${e.message}`);
}
};
public async getAlbum({ mbId }: { mbId: string }): Promise<LidarrAlbum[]> {
public async getAlbum({
artistId,
foreignAlbumId,
albumId,
}: {
artistId?: number;
foreignAlbumId?: string;
albumId?: number;
}): Promise<LidarrAlbum[]> {
try {
const response = await this.axios.get<LidarrAlbum[]>('/album', {
params: {
foreignAlbumId: mbId,
artistId,
foreignAlbumId,
albumId,
},
});
if (response.data.length > 0) {
return response.data;
}
throw new Error('Album not found');
return response.data;
} catch (e) {
logger.error('Error retrieving album by MUSICBRAINZ ID', {
label: 'Lidarr API',
errorMessage: e.message,
mbId: mbId,
});
throw new Error('Album not found');
throw new Error(`[Lidarr] Failed to retrieve album: ${e.message}`);
}
}
@ -176,7 +218,9 @@ class LidarrAPI extends ServarrBase<{ musicId: number }> {
options: LidarrAlbumOptions
): Promise<LidarrAlbum> => {
try {
const albums = await this.getAlbum({ mbId: options.mbId.toString() });
const albums = await this.getAlbum({
foreignAlbumId: options.mbId.toString(),
});
if (albums.length > 0) {
logger.info(
'Album is already monitored in Lidarr. Skipping add and returning success',

@ -1382,7 +1382,7 @@ export class MediaRequest {
const musicbrainz = new MusicBrainz();
const lidarr = new LidarrAPI({
apiKey: lidarrSettings.apiKey,
url: LidarrAPI.buildUrl(lidarrSettings, '/api/v3'),
url: LidarrAPI.buildUrl(lidarrSettings, '/api/v1'),
});
const release = await musicbrainz.getRelease(String(this.media.mbId));

@ -7,7 +7,9 @@ import type {
mbReleaseGroupType,
mbWork,
} from '@server/api/musicbrainz/interfaces';
import getPosterFromMB from '@server/api/musicbrainz/poster';
import getPosterFromMB, {
getFanartFromMB,
} from '@server/api/musicbrainz/poster';
import type {
TmdbCollectionResult,
TmdbMovieDetails,
@ -155,6 +157,8 @@ export interface ArtistResult {
endDate?: string;
tags: string[];
mediaInfo?: Media;
posterPath?: string;
fanartPath?: string;
}
export type Results =
@ -245,89 +249,111 @@ export const mapPersonResult = (
}),
});
export const mapReleaseGroupResult = (
export const mapReleaseGroupResult = async (
releaseGroupResult: mbReleaseGroup,
media?: Media
): ReleaseGroupResult => {
): Promise<ReleaseGroupResult> => {
return {
id: releaseGroupResult.id,
mediaType: releaseGroupResult.media_type,
type: releaseGroupResult.type,
title: releaseGroupResult.title,
artist: releaseGroupResult.artist.map((artist) => mapArtistResult(artist)),
releases: (releaseGroupResult.releases ?? []).map((release) =>
mapReleaseResult(release)
artist: await Promise.all(
releaseGroupResult.artist.map((artist) => mapArtistResult(artist))
),
releases: await Promise.all(
(releaseGroupResult.releases ?? []).map((release) =>
mapReleaseResult(release)
)
),
tags: releaseGroupResult.tags,
posterPath: getPosterFromMB(releaseGroupResult),
posterPath: await getPosterFromMB(releaseGroupResult),
mediaInfo: media ?? undefined,
};
};
export const mapArtistResult = (
export const mapArtistResult = async (
artist: mbArtist,
media?: Media
): ArtistResult => ({
): Promise<ArtistResult> => ({
id: artist.id,
mediaType: 'artist',
name: artist.name,
type: artist.type,
releases: Array.isArray(artist.releases)
? artist.releases.map((release) => mapReleaseResult(release))
: [],
recordings: Array.isArray(artist.recordings)
? artist.recordings.map((recording) => mapRecordingResult(recording))
: [],
releaseGroups: Array.isArray(artist.releaseGroups)
? artist.releaseGroups.map((releaseGroup) =>
mapReleaseGroupResult(releaseGroup)
)
: [],
works: Array.isArray(artist.works)
? artist.works.map((work) => mapWorkResult(work))
: [],
releases: await Promise.all(
Array.isArray(artist.releases)
? artist.releases.map((release) => mapReleaseResult(release))
: []
),
recordings: await Promise.all(
Array.isArray(artist.recordings)
? artist.recordings.map((recording) => mapRecordingResult(recording))
: []
),
releaseGroups: await Promise.all(
Array.isArray(artist.releaseGroups)
? artist.releaseGroups.map((releaseGroup) =>
mapReleaseGroupResult(releaseGroup)
)
: []
),
works: await Promise.all(
Array.isArray(artist.works)
? artist.works.map((work) => mapWorkResult(work))
: []
),
tags: artist.tags,
mediaInfo: media ?? undefined,
posterPath: await getPosterFromMB(artist),
fanartPath: await getFanartFromMB(artist),
});
export const mapReleaseResult = (
export const mapReleaseResult = async (
release: mbRelease,
media?: Media
): ReleaseResult => ({
): Promise<ReleaseResult> => ({
id: release.id,
mediaType: release.media_type,
title: release.title,
posterPath: getPosterFromMB(release),
artist: release.artist.map((artist) => mapArtistResult(artist)),
posterPath: await getPosterFromMB(release),
artist: await Promise.all(
release.artist.map((artist) => mapArtistResult(artist))
),
date: release.date,
tracks: Array.isArray(release.tracks)
? release.tracks.map((track) => mapRecordingResult(track))
: [],
tracks: await Promise.all(
Array.isArray(release.tracks)
? release.tracks.map((track) => mapRecordingResult(track))
: []
),
tags: release.tags,
mediaInfo: media,
});
export const mapRecordingResult = (
export const mapRecordingResult = async (
recording: mbRecording
): RecordingResult => ({
): Promise<RecordingResult> => ({
id: recording.id,
mediaType: recording.media_type,
title: recording.title,
artist: recording.artist.map((artist) => mapArtistResult(artist)),
artist: await Promise.all(
recording.artist.map((artist) => mapArtistResult(artist))
),
length: recording.length,
firstReleased: recording.firstReleased,
tags: recording.tags,
});
export const mapWorkResult = (work: mbWork): WorkResult => ({
export const mapWorkResult = async (work: mbWork): Promise<WorkResult> => ({
id: work.id,
mediaType: work.media_type,
title: work.title,
artist: work.artist.map((artist) => mapArtistResult(artist)),
artist: await Promise.all(
work.artist.map((artist) => mapArtistResult(artist))
),
tags: work.tags,
});
export const mapSearchResults = (
export const mapSearchResults = async (
results: (
| TmdbMovieResult
| TmdbTvResult
@ -340,49 +366,52 @@ export const mapSearchResults = (
| mbWork
)[],
media?: Media[]
): Results[] =>
results.map((result) => {
switch (result.media_type) {
case 'movie':
return mapMovieResult(
result,
media?.find(
(req) =>
req.tmdbId === result.id && req.mediaType === MainMediaType.MOVIE
)
);
case 'tv':
return mapTvResult(
result,
media?.find(
(req) =>
req.tmdbId === result.id && req.mediaType === MainMediaType.TV
)
);
case 'collection':
return mapCollectionResult(result);
case 'person':
return mapPersonResult(result);
case 'release-group':
return mapReleaseGroupResult(
result,
media?.find((req) => req.mbId === result.id)
);
case 'release':
return mapReleaseResult(
result,
media?.find((req) => req.mbId === result.id)
);
case 'recording':
return mapRecordingResult(result);
case 'work':
return mapWorkResult(result);
case 'artist':
return mapArtistResult(result);
default:
return result;
}
});
): Promise<Results[]> =>
await Promise.all(
results.map((result) => {
switch (result.media_type) {
case 'movie':
return mapMovieResult(
result,
media?.find(
(req) =>
req.tmdbId === result.id &&
req.mediaType === MainMediaType.MOVIE
)
);
case 'tv':
return mapTvResult(
result,
media?.find(
(req) =>
req.tmdbId === result.id && req.mediaType === MainMediaType.TV
)
);
case 'collection':
return mapCollectionResult(result);
case 'person':
return mapPersonResult(result);
case 'release-group':
return mapReleaseGroupResult(
result,
media?.find((req) => req.mbId === result.id)
);
case 'release':
return mapReleaseResult(
result,
media?.find((req) => req.mbId === result.id)
);
case 'recording':
return mapRecordingResult(result);
case 'work':
return mapWorkResult(result);
case 'artist':
return mapArtistResult(result);
default:
return result;
}
})
);
export const mapMovieDetailsToResult = (
movieDetails: TmdbMovieDetails

@ -863,28 +863,30 @@ discoverRoutes.get('/musics', async (req, res, next) => {
limit: 20,
offset: (Number(query.page) - 1) * 20,
});
const tmdbIds = [] as number[];
const mbIds = results.map((result) => result.id);
const media = await Media.getRelatedMedia(tmdbIds, mbIds);
const media = await Media.getRelatedMedia([], mbIds);
return res.status(200).json({
page: query.page,
results: results.map((result) =>
mapReleaseGroupResult(
result,
media.find(
(med) => med.mbId === result.id && med.mediaType === MediaType.MUSIC
results: await Promise.all(
results.map((result) =>
mapReleaseGroupResult(
result,
media.find(
(med) =>
med.mbId === result.id && med.mediaType === MediaType.MUSIC
)
)
)
),
});
} catch (e) {
logger.debug('Something went wrong retrieving popular series', {
logger.debug('Something went wrong retrieving artists', {
label: 'API',
errorMessage: e.message,
});
return next({
status: 500,
message: 'Unable to retrieve popular series.',
message: 'Unable to retrieve artists.',
});
}
});

@ -15,16 +15,16 @@ musicRoutes.get('/artist/:id', async (req, res, next) => {
const media = await Media.getMedia(artist.id, MediaType.MUSIC);
return res.status(200).json(mapArtistResult(artist, media));
return res.status(200).json(await mapArtistResult(artist, media));
} catch (e) {
logger.debug('Something went wrong retrieving movie', {
logger.debug('Something went wrong retrieving artist', {
label: 'API',
errorMessage: e.message,
movieId: req.params.id,
artistId: req.params.id,
});
return next({
status: 500,
message: 'Unable to retrieve movie.',
message: 'Unable to retrieve artist.',
});
}
});

@ -41,7 +41,7 @@ searchRoutes.get('/', async (req, res, next) => {
page: results.page,
totalPages: results.total_pages,
totalResults: results.total_results,
results: mapSearchResults(results.results, media),
results: await mapSearchResults(results.results, media),
});
} catch (e) {
logger.debug('Something went wrong retrieving search results', {

@ -28,7 +28,12 @@ declare module 'nodebrainz' {
works: Work[];
}
export default class BaseNodeBrainz {
constructor(options: { userAgent: string });
constructor(options: {
userAgent: string;
retryOn: boolean;
retryDelay: number;
retryCount: number;
});
artist(
artistId: string,
{ inc }: { inc: string },

@ -5,21 +5,32 @@ import useVerticalScroll from '@app/hooks/useVerticalScroll';
import globalMessages from '@app/i18n/globalMessages';
import type { WatchlistItem } from '@server/interfaces/api/discoverInterfaces';
import type {
ArtistResult,
CollectionResult,
MovieResult,
PersonResult,
TvResult,
MusicResult,
ArtistResult,
ReleaseResult,
PersonResult,
RecordingResult,
ReleaseGroupResult,
ReleaseResult,
TvResult,
WorkResult,
RecordingResult,
} from '@server/models/Search';
import { useIntl } from 'react-intl';
type ListViewProps = {
items?: (TvResult | MovieResult | PersonResult | CollectionResult | MusicResult | ArtistResult | ReleaseResult | ReleaseGroupResult | WorkResult | RecordingResult)[];
items?: (
| TvResult
| MovieResult
| PersonResult
| CollectionResult
| MusicResult
| ArtistResult
| ReleaseResult
| ReleaseGroupResult
| WorkResult
| RecordingResult
)[];
plexItems?: WatchlistItem[];
isEmpty?: boolean;
isLoading?: boolean;
@ -49,9 +60,9 @@ const ListView = ({
return (
<li key={`${title.ratingKey}-${index}`}>
<TmdbTitleCard
id={title.tmdbId}
tmdbId={title.tmdbId}
type={title.mediaType}
id={Number(title.tmdbId)}
tmdbId={Number(title.tmdbId)}
type={title.mediaType as 'movie' | 'tv'}
canExpand
/>
</li>
@ -124,6 +135,7 @@ const ListView = ({
<TitleCard
id={title.id}
title={title.name}
image={title.posterPath}
mediaType={title.mediaType}
canExpand
/>

@ -2,12 +2,10 @@ import Slider from '@app/components/Slider';
import MusicTitleCard from '@app/components/TitleCard/MusicTitleCard';
import TmdbTitleCard from '@app/components/TitleCard/TmdbTitleCard';
import { Permission, useUser } from '@app/hooks/useUser';
import { SecondaryType } from '@server/constants/media';
import type { MediaResultsResponse } from '@server/interfaces/api/mediaInterfaces';
import { defineMessages, useIntl } from 'react-intl';
import useSWR from 'swr';
const messages = defineMessages({
recentlyAdded: 'Recently Added',
recentlyAddedMusic: 'Recently Added Music',
@ -30,9 +28,12 @@ const RecentlyAddedSlider = () => {
return null;
}
const videoMedias = (media?.results ?? []).filter((item) => ["movie", "tv"].includes(item.mediaType))
const musicMedias = (media?.results ?? []).filter((item) => !["movie", "tv"].includes(item.mediaType))
console.log(musicMedias)
const videoMedias = (media?.results ?? []).filter((item) =>
['movie', 'tv'].includes(item.mediaType)
);
const musicMedias = (media?.results ?? []).filter(
(item) => !['movie', 'tv'].includes(item.mediaType)
);
return (
<>
<div className="slider-header">
@ -43,17 +44,15 @@ const RecentlyAddedSlider = () => {
<Slider
sliderKey="media"
isLoading={!media}
items={
videoMedias.map((item) => (
<TmdbTitleCard
key={`media-slider-item-${item.id}`}
id={item.id}
tmdbId={item.tmdbId as number}
tvdbId={item.tvdbId}
type={item.mediaType as 'movie' | 'tv'}
/>
)
)}
items={videoMedias.map((item) => (
<TmdbTitleCard
key={`media-slider-item-${item.id}`}
id={item.id}
tmdbId={item.tmdbId as number}
tvdbId={item.tvdbId}
type={item.mediaType as 'movie' | 'tv'}
/>
))}
/>
<div className="slider-header">
<div className="slider-title">
@ -64,16 +63,14 @@ const RecentlyAddedSlider = () => {
<Slider
sliderKey="media"
isLoading={!media}
items={
musicMedias.map((item) => (
<MusicTitleCard
key={`media-slider-item-${item.id}`}
id={item.id}
mbId={item.mbId ?? ''}
//type={item.secondaryType as SecondaryType}
/>
)
)}
items={musicMedias.map((item) => (
<MusicTitleCard
key={`media-slider-item-${item.id}`}
id={item.id}
mbId={item.mbId ?? ''}
//type={item.secondaryType as SecondaryType}
/>
))}
/>
</>
);

@ -186,6 +186,17 @@ const MusicDetails = ({
}}
>
<div className="media-page-bg-image">
{data.mediaType === SecondaryType.ARTIST && (
<CachedImage
alt=""
src={`https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/${
(data as ArtistResult).fanartPath
}`}
layout="fill"
objectFit="cover"
priority
/>
)}
<div
className="absolute inset-0"
style={{
@ -207,7 +218,8 @@ const MusicDetails = ({
<CachedImage
src={
data.mediaType === SecondaryType.RELEASE ||
data.mediaType === SecondaryType.RELEASE_GROUP
data.mediaType === SecondaryType.RELEASE_GROUP ||
data.mediaType === SecondaryType.ARTIST
? (data.posterPath as string)
: ''
}

@ -39,16 +39,19 @@ const Error = ({ id, tmdbId, tvdbId, type, canExpand }: ErrorCardProps) => {
<div
className="relative transform-gpu cursor-default overflow-hidden rounded-xl bg-gray-800 bg-cover shadow outline-none ring-1 ring-gray-700 transition duration-300"
style={{
paddingBottom: '150%',
aspectRatio: type === 'music' ? '1/1' : undefined,
paddingBottom: type !== 'music' ? '150%' : undefined,
}}
>
<div className="absolute inset-0 h-full w-full overflow-hidden">
<div className="absolute left-0 right-0 flex items-center justify-between p-2">
<div
className={`pointer-events-none z-40 rounded-full shadow ${
type === 'movie' ? 'bg-blue-500' :
type === 'tv' ? 'bg-purple-600'
: 'bg-green-600'
type === 'movie'
? 'bg-blue-500'
: type === 'tv'
? 'bg-purple-600'
: 'bg-green-600'
}`}
>
<div className="flex h-4 items-center px-2 py-2 text-center text-xs font-medium uppercase tracking-wider text-white sm:h-5">
@ -101,7 +104,9 @@ const Error = ({ id, tmdbId, tvdbId, type, canExpand }: ErrorCardProps) => {
>
<div className="flex items-center">
<span className="mr-2 font-bold text-gray-400">
{intl.formatMessage(messages.tmdbid)}
{intl.formatMessage(
type === 'music' ? messages.mbId : messages.tmdbid
)}
</span>
{tmdbId}
</div>

@ -1,11 +1,13 @@
import TitleCard from '@app/components/TitleCard';
import { Permission, useUser } from '@app/hooks/useUser';
import { SecondaryType } from '@server/constants/media';
import type { ArtistResult,
ReleaseGroupResult,
ReleaseResult,
WorkResult,
RecordingResult } from '@server/models/Search';
import type {
ArtistResult,
RecordingResult,
ReleaseGroupResult,
ReleaseResult,
WorkResult,
} from '@server/models/Search';
import { useInView } from 'react-intersection-observer';
import useSWR from 'swr';
@ -28,25 +30,25 @@ const MusicTitleCard = ({
triggerOnce: true,
});
const url = `/api/v1/music/${type}/${mbId}`;
const { data, error } = useSWR<ArtistResult | ReleaseGroupResult | ReleaseResult | WorkResult | RecordingResult>(
inView ? `${url}` : null
);
const { data, error } = useSWR<
| ArtistResult
| ReleaseGroupResult
| ReleaseResult
| WorkResult
| RecordingResult
>(inView ? `${url}` : null);
if (!data && !error) {
return (
<div ref={ref}>
<TitleCard.Placeholder canExpand={canExpand} />
<TitleCard.Placeholder canExpand={canExpand} type="music" />
</div>
);
}
if (!data) {
return hasPermission(Permission.ADMIN) ? (
<TitleCard.ErrorCard
id={id}
mbId={mbId}
type='music'
/>
<TitleCard.ErrorCard id={id} mbId={mbId} type="music" />
) : null;
}
@ -55,21 +57,27 @@ const MusicTitleCard = ({
return (
<TitleCard
id={mbId}
image={data.posterPath}
status={newData.mediaInfo?.status}
title={newData.name}
mediaType={data.mediaType}
canExpand={canExpand}
/>
);
} else if (data.mediaType === 'release-group' || data.mediaType === 'release') {
return (<TitleCard
} else if (
data.mediaType === 'release-group' ||
data.mediaType === 'release'
) {
return (
<TitleCard
id={mbId}
image={data.posterPath}
status={data.mediaInfo?.status}
title={data.title}
mediaType={data.mediaType}
canExpand={canExpand}
/>)
/>
);
}
return null;
};

@ -1,15 +1,24 @@
interface PlaceholderProps {
canExpand?: boolean;
type?: 'music' | 'movie' | 'tv';
}
const Placeholder = ({ canExpand = false }: PlaceholderProps) => {
const Placeholder = ({
canExpand = false,
type = 'movie',
}: PlaceholderProps) => {
return (
<div
className={`relative animate-pulse rounded-xl bg-gray-700 ${
canExpand ? 'w-full' : 'w-36 sm:w-36 md:w-44'
}`}
>
<div className="w-full" style={{ paddingBottom: '150%' }} />
<div
className="w-full"
style={
type === 'music' ? { aspectRatio: '1/1' } : { paddingBottom: '150%' }
}
/>
</div>
);
};

@ -18,7 +18,7 @@ import { Fragment, useCallback, useEffect, useState } from 'react';
import { useIntl } from 'react-intl';
interface TitleCardProps {
id: number|string;
id: number | string;
image?: string;
summary?: string;
year?: string;
@ -75,7 +75,9 @@ const TitleCard = ({
Permission.REQUEST,
mediaType === 'movie' || mediaType === 'collection'
? Permission.REQUEST_MOVIE
: (mediaType === 'tv' ? Permission.REQUEST_TV : Permission.REQUEST_MUSIC),
: mediaType === 'tv'
? Permission.REQUEST_TV
: Permission.REQUEST_MUSIC,
],
{ type: 'or' }
);
@ -87,10 +89,10 @@ const TitleCard = ({
>
<RequestModal
tmdbId={tmdbOrMbId ? (id as number) : -1}
mbId={tmdbOrMbId ? "" : (id as string)}
mbId={tmdbOrMbId ? '' : (id as string)}
show={showRequestModal}
type={
tmdbOrMbId ? mediaType as ('collection' | 'movie' | 'tv') : 'music'
tmdbOrMbId ? (mediaType as 'collection' | 'movie' | 'tv') : 'music'
}
onComplete={requestComplete}
onUpdating={requestUpdating}
@ -102,7 +104,7 @@ const TitleCard = ({
? 'scale-105 shadow-lg ring-gray-500'
: 'scale-100 shadow ring-gray-700'
}`}
style={tmdbOrMbId ?{paddingBottom : '150%'} : {aspectRatio: '1/1'}}
style={tmdbOrMbId ? { paddingBottom: '150%' } : { aspectRatio: '1/1' }}
onMouseEnter={() => {
if (!isTouch) {
setShowDetail(true);
@ -124,7 +126,9 @@ const TitleCard = ({
alt=""
src={
image
? (tmdbOrMbId ? `https://image.tmdb.org/t/p/w300_and_h450_face${image}` : image)
? tmdbOrMbId
? `https://image.tmdb.org/t/p/w300_and_h450_face${image}`
: image
: `/images/overseerr_poster_not_found_logo_top.png`
}
layout="fill"
@ -147,8 +151,7 @@ const TitleCard = ({
? intl.formatMessage(globalMessages.collection)
: mediaType === 'tv'
? intl.formatMessage(globalMessages.tvshow)
: intl.formatMessage(globalMessages.music)
}
: intl.formatMessage(globalMessages.music)}
</div>
</div>
{currentStatus && currentStatus !== MediaStatus.UNKNOWN && (
@ -188,7 +191,11 @@ const TitleCard = ({
>
<div className="absolute inset-0 overflow-hidden rounded-xl">
<Link
href={tmdbOrMbId ? `/${mediaType}/${id}` : `/music/${mediaType}/${id as string}`}
href={
tmdbOrMbId
? `/${mediaType}/${id}`
: `/music/${mediaType}/${id as string}`
}
>
<a
className="absolute inset-0 h-full w-full cursor-pointer overflow-hidden text-left"

Loading…
Cancel
Save