Skip to content

Commit

Permalink
feat(#187): fetch missing media posters from tmdb
Browse files Browse the repository at this point in the history
resolves #187
  • Loading branch information
RaunoT authored Jul 28, 2024
1 parent de1bca3 commit ce697aa
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 41 deletions.
14 changes: 9 additions & 5 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@ const nextConfig = {
protocol: 'https',
hostname: 'plex.tv',
},
{
protocol: 'https',
hostname: 'image.tmdb.org',
},
],
},
logging: {
fetches: {
fullUrl: true,
},
},
// logging: {
// fetches: {
// fullUrl: true,
// },
// },
async headers() {
return [
{
Expand Down
19 changes: 6 additions & 13 deletions src/app/api/image/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,19 @@ export async function GET(request: Request) {
return new Response('URL parameter is missing', { status: 400 })
}

const response = await fetch(url)
const res = await fetch(url)

if (!response.ok) {
if (!res.ok) {
return new Response('Failed to fetch the image', {
status: response.status,
status: res.status,
})
}

const contentType = response.headers.get('Content-Type')
if (!contentType || !contentType.startsWith('image/')) {
return new Response('The requested resource is not a valid image', {
status: 400,
})
}

const headers = new Headers(response.headers)
const body = await response.arrayBuffer()
const headers = new Headers(res.headers)
const body = await res.arrayBuffer()

return new Response(body, {
status: response.status,
status: res.status,
headers: headers,
})
} catch (error) {
Expand Down
15 changes: 9 additions & 6 deletions src/components/MediaItem/MediaItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,19 @@ export default function MediaItem({
settings,
}: Props) {
const tautulliUrl = settings.connection.tautulliUrl
const posterSrc = `${tautulliUrl}/pms_image_proxy?img=${
type === 'users' ? data.user_thumb : data.thumb
}&width=300`
const isTmdbPoster = data.thumb?.startsWith('https://image.tmdb.org')
const posterSrc = isTmdbPoster
? data.thumb
: `/api/image?url=${encodeURIComponent(
`${tautulliUrl}/pms_image_proxy?img=${
type === 'users' ? data.user_thumb : data.thumb
}&width=300`,
)}`
const [dataKey, setDataKey] = useState<number>(0)
const titleContainerRef = useRef<HTMLDivElement>(null)
const isOverseerrActive =
settings.connection.overseerrUrl && settings.connection.overseerrApiKey
const [imageSrc, setImageSrc] = useState(
`/api/image?url=${encodeURIComponent(posterSrc)}`,
)
const [imageSrc, setImageSrc] = useState(posterSrc)

useEffect(() => {
setDataKey((prevDataKey) => prevDataKey + 1)
Expand Down
16 changes: 11 additions & 5 deletions src/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ export type RewindStory = {
}

export type TmdbItem = {
results: [{ id: number; vote_average: number; first_air_date: string }]
results: [
{
id: number
vote_average: number
first_air_date: number
},
]
}

export type TmdbExternalId = { imdb_id: string }
Expand All @@ -53,16 +59,16 @@ export type TautulliItem = {

export type TautulliItemRow = {
title: string
year: number
year: number | null
total_plays: number
total_duration: number
users_watched: number | undefined
rating_key: number
thumb: string
is_deleted: boolean
rating: string
tmdb_id: number
imdb_id: string
rating: string | null
tmdb_id: number | null
imdb_id: string | null
user_thumb: string
user: string
requests: number
Expand Down
39 changes: 27 additions & 12 deletions src/utils/getMediaAdditionalData.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import {
TautulliItem,
TautulliItemRow,
TmdbExternalId,
TmdbItem,
} from '@/types'
import { TautulliItemRow, TmdbExternalId, TmdbItem } from '@/types'
import fetchTautulli from './fetchTautulli'
import fetchTmdb from './fetchTmdb'
import getSettings from './getSettings'

export default async function getMediaAdditionalData(
media: TautulliItemRow[],
Expand All @@ -20,7 +16,9 @@ export default async function getMediaAdditionalData(

const additionalData = await Promise.all(
ratingKeys.map(async (key, i) => {
const mediaTautulli = await fetchTautulli<TautulliItem>(
// TODO: We're basically only doing this fetch to determine if the item was deleted
// Maybe there's a better way to do this?
const mediaTautulli = await fetchTautulli<TautulliItemRow>(
'get_metadata',
{
rating_key: key,
Expand All @@ -31,10 +29,11 @@ export default async function getMediaAdditionalData(
// Tautulli doesn't return rating for removed items, so we're using TMDB
const mediaTmdb = await fetchTmdb<TmdbItem>(`search/${type}`, {
query: media[i].title,
first_air_date_year: type === 'tv' ? '' : media[i].year,
first_air_date_year: type === 'tv' ? '' : media[i].year || '',
})
const tmdbResult = mediaTmdb?.results?.[0]
const tmdbId = tmdbResult?.id
let poster = mediaTautulliData?.thumb || ''
let imdbId = null

if (tmdbId) {
Expand All @@ -43,6 +42,20 @@ export default async function getMediaAdditionalData(
)
}

const settings = await getSettings()
const tautulliUrl = settings.connection.tautulliUrl

// Test if thumb exists, if not, fetch from TMDB
if (!poster && tautulliUrl) {
const tmdbImage = await fetchTmdb<{ poster_path: string }>(
`${type}/${tmdbId}`,
)

if (tmdbImage?.poster_path) {
poster = `https://image.tmdb.org/t/p/w300/${tmdbImage.poster_path}`
}
}

return {
year: tmdbResult?.first_air_date
? new Date(tmdbResult.first_air_date).getFullYear()
Expand All @@ -55,18 +68,20 @@ export default async function getMediaAdditionalData(
: null,
tmdb_id: tmdbId || null,
imdb_id: imdbId?.imdb_id || null,
thumb: poster,
}
}),
)

media.map((mediaItem, i) => {
mediaItem.is_deleted = additionalData[i].is_deleted
mediaItem.rating = additionalData[i].rating || '0'
mediaItem.tmdb_id = additionalData[i].tmdb_id || 0
mediaItem.imdb_id = additionalData[i].imdb_id || '0'
mediaItem.rating = additionalData[i].rating
mediaItem.tmdb_id = additionalData[i].tmdb_id
mediaItem.imdb_id = additionalData[i].imdb_id
mediaItem.thumb = additionalData[i].thumb

if (type === 'tv') {
mediaItem.year = additionalData[i].year || 0
mediaItem.year = additionalData[i].year

if (usersWatchedData) {
const watchedData = usersWatchedData.find(
Expand Down

0 comments on commit ce697aa

Please sign in to comment.