fix: image transforms via Sharp, model photos crash, video duration
All checks were successful
Build and Push Backend Image / build (push) Successful in 46s
Build and Push Frontend Image / build (push) Successful in 5m7s

- Backend: add Sharp image transform endpoint (/assets/:id?transform=X)
  with presets: mini(64), thumbnail(200), preview(480), medium(960), banner(1280)
  Transformed images are cached as webp next to originals
- Frontend: fix model photos crash (p.directus_files_id → p)
- Frontend: fix model banner URL (data.model.banner.id → data.model.banner)
- Frontend: fix video duration display (video.movie.duration → video.movie_file?.duration)
  across models/[slug], videos, videos/[slug], and home pages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 20:56:33 +01:00
parent 273aa42510
commit 05cb6a66e3
8 changed files with 321 additions and 18 deletions

View File

@@ -144,7 +144,7 @@ const { data } = $props();
<div
class="absolute bottom-2 left-2 text-white text-sm font-medium"
>
{formatVideoDuration(video.movie.duration)}
{#if video.movie_file?.duration}{formatVideoDuration(video.movie_file.duration)}{/if}
</div>
<!-- <div
class="absolute top-2 right-2 bg-black/50 text-white text-xs px-2 py-1 rounded-full"

View File

@@ -22,9 +22,9 @@ const { data } = $props();
let images = $derived(
data.model.photos.map((p) => ({
...p.directus_files_id,
url: getAssetUrl(p.directus_files_id.id),
thumbnail: getAssetUrl(p.directus_files_id.id, "thumbnail"),
...p,
url: getAssetUrl(p.id),
thumbnail: getAssetUrl(p.id, "thumbnail"),
})),
);
@@ -52,7 +52,7 @@ let totalPlays = $derived(
<div class="relative h-64 md:h-80 overflow-hidden bg-gradient-to-br from-primary to-accent">
{#if data.model.banner}
<img
src={getAssetUrl(data.model.banner.id, "banner")}
src={getAssetUrl(data.model.banner, "banner")}
alt={$_(data.model.artist_name)}
class="w-full h-full object-cover"
/>
@@ -243,7 +243,7 @@ let totalPlays = $derived(
<div
class="absolute bottom-2 left-2 text-white text-sm font-medium"
>
{formatVideoDuration(video.movie.duration)}
{#if video.movie_file?.duration}{formatVideoDuration(video.movie_file.duration)}{/if}
</div>
<!-- <div
class="absolute top-2 right-2 bg-black/50 text-white text-xs px-2 py-1 rounded-full"

View File

@@ -34,11 +34,11 @@ const filteredVideos = $derived(() => {
const matchesCategory = categoryFilter === "all";
const matchesDuration =
durationFilter === "all" ||
(durationFilter === "short" && video.movie.duration < 10 * 60) ||
(durationFilter === "short" && (video.movie_file?.duration ?? 0) < 10 * 60) ||
(durationFilter === "medium" &&
video.movie.duration >= 10 * 60 &&
video.movie.duration < 20 * 60) ||
(durationFilter === "long" && video.movie.duration >= 20 * 60);
(video.movie_file?.duration ?? 0) >= 10 * 60 &&
(video.movie_file?.duration ?? 0) < 20 * 60) ||
(durationFilter === "long" && (video.movie_file?.duration ?? 0) >= 20 * 60);
return matchesSearch && matchesCategory && matchesDuration;
})
.sort((a, b) => {
@@ -50,7 +50,7 @@ const filteredVideos = $derived(() => {
return (b.likes_count || 0) - (a.likes_count || 0);
if (sortBy === "most_played")
return (b.plays_count || 0) - (a.plays_count || 0);
if (sortBy === "duration") return b.movie.duration - a.movie.duration;
if (sortBy === "duration") return (b.movie_file?.duration ?? 0) - (a.movie_file?.duration ?? 0);
return a.title.localeCompare(b.title);
});
});
@@ -220,7 +220,7 @@ const filteredVideos = $derived(() => {
<div
class="absolute bottom-3 left-3 bg-black/70 text-white text-sm px-2 py-1 rounded font-medium"
>
{formatVideoDuration(video.movie.duration)}
{#if video.movie_file?.duration}{formatVideoDuration(video.movie_file.duration)}{/if}
</div>
<!-- Premium Badge -->

View File

@@ -221,7 +221,7 @@ let showPlayer = $state(false);
<div
class="absolute bottom-4 left-4 bg-black/70 text-white px-3 py-1 rounded font-medium"
>
{formatVideoDuration(data.video.movie.duration)}
{#if data.video.movie_file?.duration}{formatVideoDuration(data.video.movie_file.duration)}{/if}
</div>
{#if data.video.premium}
<div