Skip to content

Commit ed14656

Browse files
committed
fix: put description back in video_data, remove deprecated link_description from CTA
Meta API v24 rejects link_description in call_to_action.value (deprecated). The earlier error about description in video_data was caused by the link field also being present. With link removed, description works in video_data. Also moved thumbnail auto-fetch before the asset_feed_spec branch so both paths get a thumbnail, and added image_url to the asset_feed_spec anchor.
1 parent 23cda31 commit ed14656

File tree

2 files changed

+32
-25
lines changed

2 files changed

+32
-25
lines changed

meta_ads_mcp/core/ads.py

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,21 @@ async def create_ad_creative(
963963
# Track if this is a video creative
964964
is_video = bool(video_id)
965965

966+
# Meta API v24 REQUIRES a thumbnail (image_hash or image_url) in video_data.
967+
# If the caller didn't provide one, auto-fetch from the video object.
968+
if is_video and not thumbnail_url:
969+
try:
970+
video_info = await make_api_request(
971+
video_id, access_token, {"fields": "picture"}
972+
)
973+
if isinstance(video_info, dict) and "picture" in video_info:
974+
thumbnail_url = video_info["picture"]
975+
logger.info(f"Auto-fetched video thumbnail: {thumbnail_url[:80]}...")
976+
else:
977+
logger.warning(f"Could not auto-fetch thumbnail for video {video_id}: {video_info}")
978+
except Exception as e:
979+
logger.warning(f"Failed to auto-fetch thumbnail for video {video_id}: {e}")
980+
966981
if use_asset_feed:
967982
# Build the media array from the provided source
968983
if is_video:
@@ -1032,11 +1047,12 @@ async def create_ad_creative(
10321047
if is_video:
10331048
# video_data does NOT support "link" directly — URL goes in
10341049
# call_to_action.value.link or is handled by asset_feed_spec.link_urls.
1050+
video_anchor = {"video_id": video_id}
1051+
if thumbnail_url:
1052+
video_anchor["image_url"] = thumbnail_url
10351053
creative_data["object_story_spec"] = {
10361054
"page_id": page_id,
1037-
"video_data": {
1038-
"video_id": video_id,
1039-
}
1055+
"video_data": video_anchor
10401056
}
10411057
else:
10421058
creative_data["object_story_spec"] = {
@@ -1050,18 +1066,7 @@ async def create_ad_creative(
10501066
# Use object_story_spec with video_data for simple video creatives.
10511067
# NOTE: video_data does NOT support a "link" field directly.
10521068
# 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-
1069+
# Thumbnail auto-fetch is handled earlier (before use_asset_feed branch).
10651070
video_data = {
10661071
"video_id": video_id,
10671072
}
@@ -1075,18 +1080,15 @@ async def create_ad_creative(
10751080
if headline:
10761081
video_data["title"] = headline
10771082

1078-
# NOTE: video_data does NOT support "description" directly.
1079-
# For video creatives, description can go in
1080-
# call_to_action.value.link_description if needed.
1083+
if description:
1084+
video_data["description"] = description
10811085

10821086
# Build call_to_action with the destination URL.
10831087
# For video creatives, link_url MUST go in call_to_action.value.link
10841088
# (not as a top-level field in video_data).
10851089
cta_value = {}
10861090
if link_url:
10871091
cta_value["link"] = link_url
1088-
if description:
1089-
cta_value["link_description"] = description
10901092
if lead_gen_form_id:
10911093
cta_value["lead_gen_form_id"] = lead_gen_form_id
10921094
cta_type = call_to_action_type or ("LEARN_MORE" if link_url else None)

tests/test_video_creatives.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,9 @@ async def test_simple_video_creative_uses_video_data():
7272
assert "link" not in video_data, "link must NOT be in video_data directly"
7373
assert video_data["message"] == "Check out this video"
7474
assert video_data["title"] == "Watch Now"
75-
assert "description" not in video_data, "description is NOT supported in video_data"
75+
assert video_data["description"] == "Amazing content"
7676
assert video_data["call_to_action"]["type"] == "LEARN_MORE"
7777
assert video_data["call_to_action"]["value"]["link"] == "https://example.com/"
78-
assert video_data["call_to_action"]["value"]["link_description"] == "Amazing content"
7978

8079

8180
@pytest.mark.asyncio
@@ -219,7 +218,11 @@ async def test_video_creative_with_dof_optimization():
219218
}
220219

221220
mock_api.side_effect = [
221+
# 1) Auto-fetch video thumbnail
222+
{"picture": "https://example.com/auto-thumb.jpg"},
223+
# 2) POST create creative
222224
{"id": "creative_vid_5"},
225+
# 3) GET creative details
223226
{"id": "creative_vid_5", "name": "Video DOF", "status": "ACTIVE"}
224227
]
225228

@@ -233,14 +236,16 @@ async def test_video_creative_with_dof_optimization():
233236
access_token="test_token"
234237
)
235238

236-
creative_data = mock_api.call_args_list[0][0][2]
239+
creative_data = mock_api.call_args_list[1][0][2]
237240
afs = creative_data["asset_feed_spec"]
238241

239242
assert afs["optimization_type"] == "DEGREES_OF_FREEDOM"
240243
assert "videos" in afs
241-
assert afs["videos"] == [{"video_id": "vid_777888"}]
244+
# Auto-fetched thumbnail should be included in videos array
245+
assert afs["videos"] == [{"video_id": "vid_777888", "thumbnail_url": "https://example.com/auto-thumb.jpg"}]
242246

243-
# video_data anchor should not contain "link"
247+
# video_data anchor should have image_url and not "link"
248+
assert creative_data["object_story_spec"]["video_data"]["image_url"] == "https://example.com/auto-thumb.jpg"
244249
assert "link" not in creative_data["object_story_spec"]["video_data"]
245250

246251

0 commit comments

Comments
 (0)