160, 'sm' => 320, 'thumb' => 320, 'md' => 640, 'lg' => 1280, 'xl' => 1920, 'sq' => 400, ]; private const HEIGHTS = [ 'xs' => 90, 'sm' => 180, 'thumb' => 180, 'md' => 360, 'lg' => 720, 'xl' => 1080, 'sq' => 400, ]; /** * Present thumbnail data for an item which may be a model or an array. * Returns ['id' => int|null, 'title' => string, 'url' => string, 'srcset' => string|null] */ public static function present($item, string $size = 'md'): array { $size = self::normalizeSize($size); $id = null; $title = ''; if ($item instanceof Artwork) { $id = $item->id; $title = (string) $item->title; $url = self::resolveArtworkUrl($item, $size); return [ 'id' => $id, 'title' => $title, 'url' => $url, 'width' => self::WIDTHS[$size] ?? null, 'height' => self::HEIGHTS[$size] ?? null, 'srcset' => self::buildSrcsetFromArtwork($item), ]; } $uext = 'webp'; $isEloquent = $item instanceof \Illuminate\Database\Eloquent\Model; if ($isEloquent) { $id = $item->id ?? null; $title = $item->title ?? ($item->name ?? ''); $url = $item->thumb_url ?? $item->thumb ?? ''; $srcset = $item->thumb_srcset ?? null; if (empty($url)) { $url = self::missingUrl($size); } return [ 'id' => $id, 'title' => $title, 'url' => $url, 'width' => self::WIDTHS[$size] ?? null, 'height' => self::HEIGHTS[$size] ?? null, 'srcset' => $srcset, ]; } // If it's an object but not an Eloquent model (e.g. stdClass row), cast to array if (is_object($item)) { $item = (array) $item; } $id = $item['id'] ?? null; $title = $item['name'] ?? ''; // If array contains direct hash/thumb_ext, use CDN fromHash $hash = $item['hash'] ?? null; $thumbExt = 'webp'; if (!empty($hash) && !empty($thumbExt)) { $url = ThumbnailService::fromHash($hash, $thumbExt, $size) ?: ThumbnailService::url(null, $id, $thumbExt, 6); $srcset = ThumbnailService::srcsetFromHash($hash, $thumbExt); if (empty($url)) { $url = self::missingUrl($size); } return [ 'id' => $id, 'title' => $title, 'url' => $url, 'width' => self::WIDTHS[$size] ?? null, 'height' => self::HEIGHTS[$size] ?? null, 'srcset' => $srcset, ]; } // Fallback: ask ThumbnailService to resolve by id or file path $url = ThumbnailService::url(null, $id, $uext, 6); if (empty($url)) { $url = self::missingUrl($size); } return [ 'id' => $id, 'title' => $title, 'url' => $url, 'width' => self::WIDTHS[$size] ?? null, 'height' => self::HEIGHTS[$size] ?? null, 'srcset' => null, ]; } public static function srcsetForArtwork(Artwork $artwork): string { return self::buildSrcsetFromArtwork($artwork); } private static function resolveArtworkUrl(Artwork $artwork, string $size): string { $hash = $artwork->hash ?? null; if (!empty($hash)) { $url = ThumbnailService::fromHash((string) $hash, 'webp', $size); if (!empty($url)) { return $url; } } $filePath = $artwork->file_path ?? $artwork->file_name ?? null; if (!empty($filePath)) { $cdn = rtrim((string) config('cdn.files_url', 'https://files.skinbase.org'), '/'); $path = ltrim((string) $filePath, '/'); $pathWithoutExt = preg_replace('/\.[^.]+$/', '', $path); return sprintf('%s/%s/%s.webp', $cdn, $size, $pathWithoutExt); } return self::missingUrl($size); } private static function buildSrcsetFromArtwork(Artwork $artwork): string { $md = self::resolveArtworkUrl($artwork, 'md'); $lg = self::resolveArtworkUrl($artwork, 'lg'); $xl = self::resolveArtworkUrl($artwork, 'xl'); return implode(', ', [ $md . ' 640w', $lg . ' 1280w', $xl . ' 1920w', ]); } private static function normalizeSize(string $size): string { $size = strtolower(trim($size)); return array_key_exists($size, self::WIDTHS) ? $size : 'md'; } private static function missingUrl(string $size): string { return sprintf('%s/missing_%s.webp', self::MISSING_BASE, $size); } }