Skip to content

Commit 23cda31

Browse files
committed
fix: auto-fetch video thumbnail when not provided
Meta API v24 requires image_hash or image_url in video_data. When thumbnail_url is not provided, auto-fetch the video picture from the Meta Graph API as a fallback.
1 parent 4f7a705 commit 23cda31

File tree

2 files changed

+26
-4
lines changed

2 files changed

+26
-4
lines changed

meta_ads_mcp/core/ads.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,18 @@ async def create_ad_creative(
10501050
# Use object_story_spec with video_data for simple video creatives.
10511051
# NOTE: video_data does NOT support a "link" field directly.
10521052
# The destination URL goes in call_to_action.value.link.
1053+
1054+
# Meta API v24 REQUIRES a thumbnail (image_hash or image_url) in video_data.
1055+
# If the caller didn't provide one, auto-fetch from the video object.
1056+
if not thumbnail_url:
1057+
try:
1058+
video_info = await make_api_request(
1059+
video_id, access_token, {"fields": "picture"}
1060+
)
1061+
thumbnail_url = video_info.get("picture")
1062+
except Exception:
1063+
pass # Best-effort; Meta API will return a clear error if missing
1064+
10531065
video_data = {
10541066
"video_id": video_id,
10551067
}

tests/test_video_creatives.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ async def test_simple_video_creative_uses_video_data():
3333
}
3434

3535
mock_api.side_effect = [
36+
# 1) Auto-fetch video thumbnail (no thumbnail_url provided)
37+
{"picture": "https://example.com/auto-thumb.jpg"},
38+
# 2) POST create creative
3639
{"id": "creative_vid_1"},
40+
# 3) GET creative details
3741
{"id": "creative_vid_1", "name": "Video Creative", "status": "ACTIVE"}
3842
]
3943

@@ -49,9 +53,12 @@ async def test_simple_video_creative_uses_video_data():
4953
access_token="test_token"
5054
)
5155

52-
assert mock_api.call_count == 2
56+
assert mock_api.call_count == 3
5357

54-
creative_data = mock_api.call_args_list[0][0][2]
58+
# First call is the thumbnail auto-fetch
59+
assert mock_api.call_args_list[0][0][0] == "vid_987654"
60+
61+
creative_data = mock_api.call_args_list[1][0][2]
5562

5663
# Should use object_story_spec with video_data, NOT link_data
5764
assert "object_story_spec" in creative_data
@@ -61,6 +68,7 @@ async def test_simple_video_creative_uses_video_data():
6168

6269
video_data = creative_data["object_story_spec"]["video_data"]
6370
assert video_data["video_id"] == "vid_987654"
71+
assert video_data["image_url"] == "https://example.com/auto-thumb.jpg"
6472
assert "link" not in video_data, "link must NOT be in video_data directly"
6573
assert video_data["message"] == "Check out this video"
6674
assert video_data["title"] == "Watch Now"
@@ -121,6 +129,7 @@ async def test_video_creative_with_instagram_actor_id():
121129
}
122130

123131
mock_api.side_effect = [
132+
{"picture": "https://example.com/auto-thumb.jpg"},
124133
{"id": "creative_vid_3"},
125134
{"id": "creative_vid_3", "name": "Video IG", "status": "ACTIVE"}
126135
]
@@ -134,7 +143,7 @@ async def test_video_creative_with_instagram_actor_id():
134143
access_token="test_token"
135144
)
136145

137-
creative_data = mock_api.call_args_list[0][0][2]
146+
creative_data = mock_api.call_args_list[1][0][2]
138147
video_data = creative_data["object_story_spec"]["video_data"]
139148

140149
# For simple video creatives, instagram_actor_id should be inside video_data
@@ -319,6 +328,7 @@ async def test_video_creative_with_lead_gen():
319328
}
320329

321330
mock_api.side_effect = [
331+
{"picture": "https://example.com/auto-thumb.jpg"},
322332
{"id": "creative_vid_lead"},
323333
{"id": "creative_vid_lead", "name": "Video Lead Gen", "status": "ACTIVE"}
324334
]
@@ -333,7 +343,7 @@ async def test_video_creative_with_lead_gen():
333343
access_token="test_token"
334344
)
335345

336-
creative_data = mock_api.call_args_list[0][0][2]
346+
creative_data = mock_api.call_args_list[1][0][2]
337347
video_data = creative_data["object_story_spec"]["video_data"]
338348

339349
assert "link" not in video_data, "link must NOT be in video_data directly"

0 commit comments

Comments
 (0)