Skip to content

Commit db13051

Browse files
zigzagdevclaude
andcommitted
feat: fetch YouTube thumbnail via UNESCO API for proxy image endpoint
Instead of proxying UNESCO /document/ URLs (blocked by Cloudflare as subresource requests), fetch main_video_url from data.unesco.org API on each request, extract the YouTube video ID, and return the YouTube hqdefault thumbnail binary. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 9b53db2 commit db13051

1 file changed

Lines changed: 38 additions & 10 deletions

File tree

src/app/Packages/Features/Controller/HeritageImageController.php

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,55 @@ class HeritageImageController extends Controller
1313
{
1414
public function proxyImage(Request $request, int $id): Response|JsonResponse
1515
{
16-
$image = Image::where('world_heritage_site_id', $id)
17-
->orderByDesc('is_primary')
18-
->orderBy('sort_order')
19-
->first();
16+
$videoUrl = $this->fetchVideoUrlFromUnesco($id);
2017

21-
if ($image === null) {
22-
return response()->json(['error' => 'Image not found'], 404);
18+
if ($videoUrl === null) {
19+
return response()->json(['error' => 'No video URL found for this site'], 404);
2320
}
2421

25-
$upstream = Http::withHeaders([
26-
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
27-
])->get($image->url);
22+
$thumbnailUrl = $this->buildYoutubeThumbnailUrl($videoUrl);
23+
24+
if ($thumbnailUrl === null) {
25+
return response()->json(['error' => 'Could not extract YouTube video ID'], 422);
26+
}
27+
28+
$upstream = Http::get($thumbnailUrl);
2829

2930
if ($upstream->failed()) {
30-
return response()->json(['error' => 'Failed to fetch image from upstream'], 502);
31+
return response()->json(['error' => 'Failed to fetch thumbnail from YouTube'], 502);
3132
}
3233

3334
return response($upstream->body(), 200)
3435
->header('Content-Type', $upstream->header('Content-Type'));
3536
}
3637

38+
private function fetchVideoUrlFromUnesco(int $idNo): ?string
39+
{
40+
$response = Http::acceptJson()
41+
->get('https://data.unesco.org/api/explore/v2.1/catalog/datasets/whc001/records', [
42+
'where' => "id_no={$idNo}",
43+
'limit' => 1,
44+
'select' => 'main_video_url',
45+
]);
46+
47+
if ($response->failed()) {
48+
return null;
49+
}
50+
51+
$results = $response->json('results');
52+
53+
return $results[0]['main_video_url'] ?? null;
54+
}
55+
56+
private function buildYoutubeThumbnailUrl(string $videoUrl): ?string
57+
{
58+
if (preg_match('/(?:embed\/|v=|youtu\.be\/)([A-Za-z0-9_-]{11})/', $videoUrl, $m)) {
59+
return "https://img.youtube.com/vi/{$m[1]}/hqdefault.jpg";
60+
}
61+
62+
return null;
63+
}
64+
3765
public function proxyImageById(Request $request, int $imageId): Response|JsonResponse
3866
{
3967
$image = Image::find($imageId);

0 commit comments

Comments
 (0)