mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-08-29 01:35:39 -04:00
Refactor tmdbImageUrl to accept semantic category instead of size string
- Change primary argument from TMDB size string (`w500`, `w1280`, etc.) to category name (`posters`, `backdrops`, `stills`, `logos`, `profiles`); optional size override remains as third argument - Update all call sites across services, actions, pages, and route handlers to use the new category-based API - Update image.test.ts to match the new signature and replace size-mapping test descriptions with category-name descriptions
This commit is contained in:
@@ -20,7 +20,7 @@ export async function discoverByGenre(
|
||||
tmdbId: r.id,
|
||||
type: mediaType,
|
||||
title: (r.title ?? r.name) as string,
|
||||
posterPath: tmdbImageUrl(r.poster_path, "w500"),
|
||||
posterPath: tmdbImageUrl(r.poster_path, "posters"),
|
||||
releaseDate: (r.release_date ?? r.first_air_date ?? null) as
|
||||
| string
|
||||
| null,
|
||||
|
||||
@@ -316,6 +316,6 @@ export function getCastForTitle(titleId: string): CastMember[] {
|
||||
|
||||
return rows.map((r) => ({
|
||||
...r,
|
||||
profilePath: tmdbImageUrl(r.profilePath, "w185"),
|
||||
profilePath: tmdbImageUrl(r.profilePath, "profiles"),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -505,7 +505,7 @@ export function getRecommendationsForTitle(titleId: string) {
|
||||
tmdbId: r.tmdbId,
|
||||
type: r.type as "movie" | "tv",
|
||||
title: r.title,
|
||||
posterPath: tmdbImageUrl(r.posterPath, "w500"),
|
||||
posterPath: tmdbImageUrl(r.posterPath, "posters"),
|
||||
releaseDate: r.releaseDate,
|
||||
firstAirDate: r.firstAirDate,
|
||||
voteAverage: r.voteAverage,
|
||||
|
||||
@@ -610,7 +610,7 @@ function fetchSeasonsFromDb(titleId: string): Season[] {
|
||||
episodeNumber: ep.episodeNumber,
|
||||
name: ep.name,
|
||||
overview: ep.overview,
|
||||
stillPath: tmdbImageUrl(ep.stillPath, "w1280", "stills"),
|
||||
stillPath: tmdbImageUrl(ep.stillPath, "stills"),
|
||||
airDate: ep.airDate,
|
||||
runtimeMinutes: ep.runtimeMinutes,
|
||||
});
|
||||
@@ -763,7 +763,7 @@ function readAvailability(
|
||||
.map((a) => ({
|
||||
providerId: a.providerId,
|
||||
providerName: a.providerName,
|
||||
logoPath: tmdbImageUrl(a.logoPath, "w92"),
|
||||
logoPath: tmdbImageUrl(a.logoPath, "logos"),
|
||||
offerType: a.offerType,
|
||||
watchUrl: generateProviderUrl(a.providerId, titleName),
|
||||
}));
|
||||
@@ -852,8 +852,8 @@ export async function getOrFetchTitle(id: string): Promise<{
|
||||
overview: title.overview,
|
||||
releaseDate: title.releaseDate,
|
||||
firstAirDate: title.firstAirDate,
|
||||
posterPath: tmdbImageUrl(title.posterPath, "w500"),
|
||||
backdropPath: tmdbImageUrl(title.backdropPath, "w1280"),
|
||||
posterPath: tmdbImageUrl(title.posterPath, "posters"),
|
||||
backdropPath: tmdbImageUrl(title.backdropPath, "backdrops"),
|
||||
popularity: title.popularity,
|
||||
voteAverage: title.voteAverage,
|
||||
voteCount: title.voteCount,
|
||||
|
||||
@@ -46,7 +46,7 @@ export async function getOrFetchPerson(
|
||||
birthday: details.birthday,
|
||||
deathday: details.deathday,
|
||||
placeOfBirth: details.place_of_birth,
|
||||
profilePath: tmdbImageUrl(details.profile_path, "w185"),
|
||||
profilePath: tmdbImageUrl(details.profile_path, "profiles"),
|
||||
knownForDepartment: details.known_for_department,
|
||||
imdbId: details.imdb_id,
|
||||
};
|
||||
@@ -63,7 +63,7 @@ export async function getOrFetchPerson(
|
||||
birthday: person.birthday,
|
||||
deathday: person.deathday,
|
||||
placeOfBirth: person.placeOfBirth,
|
||||
profilePath: tmdbImageUrl(person.profilePath, "w185"),
|
||||
profilePath: tmdbImageUrl(person.profilePath, "profiles"),
|
||||
knownForDepartment: person.knownForDepartment,
|
||||
imdbId: person.imdbId,
|
||||
};
|
||||
@@ -116,7 +116,7 @@ export async function getOrFetchPersonByTmdbId(
|
||||
birthday: person.birthday,
|
||||
deathday: person.deathday,
|
||||
placeOfBirth: person.placeOfBirth,
|
||||
profilePath: tmdbImageUrl(person.profilePath, "w185"),
|
||||
profilePath: tmdbImageUrl(person.profilePath, "profiles"),
|
||||
knownForDepartment: person.knownForDepartment,
|
||||
imdbId: person.imdbId,
|
||||
};
|
||||
@@ -151,7 +151,7 @@ export function getLocalFilmography(personId: string): PersonCredit[] {
|
||||
tmdbId: r.tmdbId,
|
||||
type: r.type as "movie" | "tv",
|
||||
title: r.title,
|
||||
posterPath: tmdbImageUrl(r.posterPath, "w500"),
|
||||
posterPath: tmdbImageUrl(r.posterPath, "posters"),
|
||||
releaseDate: r.releaseDate,
|
||||
firstAirDate: r.firstAirDate,
|
||||
voteAverage: r.voteAverage,
|
||||
@@ -245,7 +245,7 @@ export async function fetchFullFilmography(
|
||||
tmdbId: c.id,
|
||||
type: c.media_type as "movie" | "tv",
|
||||
title: c.title ?? c.name ?? "Unknown",
|
||||
posterPath: tmdbImageUrl(c.poster_path, "w500"),
|
||||
posterPath: tmdbImageUrl(c.poster_path, "posters"),
|
||||
releaseDate: c.release_date ?? null,
|
||||
firstAirDate: c.first_air_date ?? null,
|
||||
voteAverage: c.vote_average,
|
||||
|
||||
+42
-31
@@ -24,54 +24,56 @@ describe("tmdbImageUrl", () => {
|
||||
});
|
||||
|
||||
test("returns null for null path", () => {
|
||||
expect(tmdbImageUrl(null)).toBeNull();
|
||||
expect(tmdbImageUrl(null, "posters")).toBeNull();
|
||||
});
|
||||
|
||||
test("returns null for undefined path", () => {
|
||||
expect(tmdbImageUrl(undefined as unknown as string | null)).toBeNull();
|
||||
expect(
|
||||
tmdbImageUrl(undefined as unknown as string | null, "posters"),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
describe("cache enabled (default)", () => {
|
||||
test("w500 maps to posters", () => {
|
||||
expect(tmdbImageUrl("/abc.jpg", "w500")).toBe(
|
||||
test("posters category", () => {
|
||||
expect(tmdbImageUrl("/abc.jpg", "posters")).toBe(
|
||||
"/api/images/posters/abc.jpg",
|
||||
);
|
||||
});
|
||||
|
||||
test("w1280 maps to backdrops", () => {
|
||||
expect(tmdbImageUrl("/backdrop.jpg", "w1280")).toBe(
|
||||
test("backdrops category", () => {
|
||||
expect(tmdbImageUrl("/backdrop.jpg", "backdrops")).toBe(
|
||||
"/api/images/backdrops/backdrop.jpg",
|
||||
);
|
||||
});
|
||||
|
||||
test("w92 maps to logos", () => {
|
||||
expect(tmdbImageUrl("/logo.png", "w92")).toBe(
|
||||
test("logos category", () => {
|
||||
expect(tmdbImageUrl("/logo.png", "logos")).toBe(
|
||||
"/api/images/logos/logo.png",
|
||||
);
|
||||
});
|
||||
|
||||
test("w185 maps to profiles", () => {
|
||||
expect(tmdbImageUrl("/profile.jpg", "w185")).toBe(
|
||||
test("profiles category", () => {
|
||||
expect(tmdbImageUrl("/profile.jpg", "profiles")).toBe(
|
||||
"/api/images/profiles/profile.jpg",
|
||||
);
|
||||
});
|
||||
|
||||
test("strips leading slash from path", () => {
|
||||
expect(tmdbImageUrl("/test.jpg")).toBe("/api/images/posters/test.jpg");
|
||||
});
|
||||
|
||||
test("handles path without leading slash", () => {
|
||||
expect(tmdbImageUrl("test.jpg")).toBe("/api/images/posters/test.jpg");
|
||||
});
|
||||
|
||||
test("explicit category override", () => {
|
||||
expect(tmdbImageUrl("/still.jpg", "w1280", "stills")).toBe(
|
||||
test("stills category", () => {
|
||||
expect(tmdbImageUrl("/still.jpg", "stills")).toBe(
|
||||
"/api/images/stills/still.jpg",
|
||||
);
|
||||
});
|
||||
|
||||
test("default size is w500 (posters)", () => {
|
||||
expect(tmdbImageUrl("/img.jpg")).toBe("/api/images/posters/img.jpg");
|
||||
test("strips leading slash from path", () => {
|
||||
expect(tmdbImageUrl("/test.jpg", "posters")).toBe(
|
||||
"/api/images/posters/test.jpg",
|
||||
);
|
||||
});
|
||||
|
||||
test("handles path without leading slash", () => {
|
||||
expect(tmdbImageUrl("test.jpg", "posters")).toBe(
|
||||
"/api/images/posters/test.jpg",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -80,25 +82,34 @@ describe("tmdbImageUrl", () => {
|
||||
process.env.IMAGE_CACHE_ENABLED = "false";
|
||||
});
|
||||
|
||||
test("returns TMDB CDN URL with default base", () => {
|
||||
expect(tmdbImageUrl("/abc.jpg", "w500")).toBe(
|
||||
test("returns TMDB CDN URL with default size for category", () => {
|
||||
expect(tmdbImageUrl("/abc.jpg", "posters")).toBe(
|
||||
"https://image.tmdb.org/t/p/w500/abc.jpg",
|
||||
);
|
||||
});
|
||||
|
||||
test("uses custom TMDB_IMAGE_BASE_URL", () => {
|
||||
process.env.TMDB_IMAGE_BASE_URL = "https://custom-cdn.example.com";
|
||||
// Need to re-import to pick up the new base URL — but since IMAGE_BASE_URL
|
||||
// is evaluated at module load time, we test with the default
|
||||
expect(tmdbImageUrl("/abc.jpg", "w1280")).toBe(
|
||||
test("uses category-specific size", () => {
|
||||
expect(tmdbImageUrl("/abc.jpg", "backdrops")).toBe(
|
||||
"https://image.tmdb.org/t/p/w1280/abc.jpg",
|
||||
);
|
||||
});
|
||||
|
||||
test("preserves size in URL", () => {
|
||||
expect(tmdbImageUrl("/img.jpg", "w185")).toBe(
|
||||
test("allows size override", () => {
|
||||
expect(tmdbImageUrl("/abc.jpg", "posters", "w300")).toBe(
|
||||
"https://image.tmdb.org/t/p/w300/abc.jpg",
|
||||
);
|
||||
});
|
||||
|
||||
test("preserves size for profiles", () => {
|
||||
expect(tmdbImageUrl("/img.jpg", "profiles")).toBe(
|
||||
"https://image.tmdb.org/t/p/w185/img.jpg",
|
||||
);
|
||||
});
|
||||
|
||||
test("preserves size for logos", () => {
|
||||
expect(tmdbImageUrl("/img.jpg", "logos")).toBe(
|
||||
"https://image.tmdb.org/t/p/w92/img.jpg",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+12
-10
@@ -3,25 +3,27 @@ import type { ImageCategory } from "@/lib/services/image-cache";
|
||||
const IMAGE_BASE_URL =
|
||||
process.env.TMDB_IMAGE_BASE_URL || "https://image.tmdb.org/t/p";
|
||||
|
||||
function sizeToCategory(size: string): ImageCategory {
|
||||
if (size === "w92") return "logos";
|
||||
if (size === "w185") return "profiles";
|
||||
if (size === "w1280") return "backdrops";
|
||||
return "posters";
|
||||
}
|
||||
const CATEGORY_SIZES: Record<ImageCategory, string> = {
|
||||
posters: "w500",
|
||||
backdrops: "w1280",
|
||||
stills: "w1280",
|
||||
logos: "w92",
|
||||
profiles: "w185",
|
||||
};
|
||||
|
||||
export function tmdbImageUrl(
|
||||
path: string | null,
|
||||
size = "w500",
|
||||
category?: ImageCategory,
|
||||
category: ImageCategory,
|
||||
sizeOverride?: string,
|
||||
) {
|
||||
if (!path) return null;
|
||||
|
||||
const size = sizeOverride ?? CATEGORY_SIZES[category];
|
||||
|
||||
if (process.env.IMAGE_CACHE_ENABLED === "false") {
|
||||
return `${IMAGE_BASE_URL}/${size}${path}`;
|
||||
}
|
||||
|
||||
const resolved = category ?? sizeToCategory(size);
|
||||
const filename = path.startsWith("/") ? path.slice(1) : path;
|
||||
return `/api/images/${resolved}/${filename}`;
|
||||
return `/api/images/${category}/${filename}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user