fix: Meta creatividades — 5 titulares, 5 textos y descripción
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -210,6 +210,7 @@ function cardsFromDaily(
|
||||
headline: agg.campaignName || agg.adName,
|
||||
primaryText: description,
|
||||
headlines: [agg.campaignName || agg.adName].filter(Boolean),
|
||||
bodies: [],
|
||||
descriptions: [description],
|
||||
assets: [] as CreativeAsset[],
|
||||
status: undefined,
|
||||
@@ -497,8 +498,9 @@ WHERE ${dateWhere}
|
||||
headline: [...headlines, ...longHeadlines].join('\n'),
|
||||
primaryText: descriptions.join('\n'),
|
||||
headlines,
|
||||
bodies: descriptions,
|
||||
longHeadlines,
|
||||
descriptions,
|
||||
descriptions: [],
|
||||
assets: entry.assets,
|
||||
linkUrl: entry.finalUrls[0],
|
||||
cta: uniq(entry.ctas)[0],
|
||||
@@ -553,8 +555,9 @@ WHERE ${dateWhere}
|
||||
headline: [...headlines, ...longHeadlines].join('\n'),
|
||||
primaryText: descriptions.join('\n'),
|
||||
headlines,
|
||||
bodies: descriptions,
|
||||
longHeadlines,
|
||||
descriptions,
|
||||
descriptions: [],
|
||||
assets,
|
||||
linkUrl: (ad?.finalUrls || []).map(str).filter(Boolean)[0],
|
||||
cta: str(rda?.callToActionText) || undefined,
|
||||
@@ -562,8 +565,7 @@ WHERE ${dateWhere}
|
||||
status: enumName(row.adGroupAd?.status),
|
||||
});
|
||||
if (paths.length) {
|
||||
// encode paths into descriptions note for zoom
|
||||
cards[cards.length - 1].descriptions = [
|
||||
cards[cards.length - 1].bodies = [
|
||||
...descriptions,
|
||||
`Path: /${paths.join('/')}`,
|
||||
];
|
||||
|
||||
@@ -48,10 +48,14 @@ export type AdCreativeCard = {
|
||||
/** Best preview URL (prefer vertical / portrait). */
|
||||
thumbnailUrl: string;
|
||||
imageUrl: string;
|
||||
/** Joined for backwards compat; prefer headlines/descriptions arrays. */
|
||||
/** Joined for backwards compat; prefer headlines/bodies/descriptions arrays. */
|
||||
headline: string;
|
||||
primaryText: string;
|
||||
/** Titulares (Meta titles / Google headlines), máx. 5. */
|
||||
headlines: string[];
|
||||
/** Textos principales (Meta bodies), máx. 5. */
|
||||
bodies: string[];
|
||||
/** Descripción del enlace (Meta descriptions), suele ser 1. */
|
||||
descriptions: string[];
|
||||
longHeadlines?: string[];
|
||||
assets: CreativeAsset[];
|
||||
@@ -122,6 +126,7 @@ function detectMediaType(creative: Record<string, unknown>): string {
|
||||
|
||||
type Extracted = {
|
||||
headlines: string[];
|
||||
bodies: string[];
|
||||
descriptions: string[];
|
||||
assets: CreativeAsset[];
|
||||
linkUrl: string;
|
||||
@@ -131,6 +136,7 @@ type Extracted = {
|
||||
|
||||
function extractCreativePack(creative: Record<string, unknown>): Extracted {
|
||||
const headlines: string[] = [];
|
||||
const bodies: string[] = [];
|
||||
const descriptions: string[] = [];
|
||||
const assets: CreativeAsset[] = [];
|
||||
const hashes: string[] = [];
|
||||
@@ -155,50 +161,56 @@ function extractCreativePack(creative: Record<string, unknown>): Extracted {
|
||||
});
|
||||
};
|
||||
|
||||
// Primary serving copy = object_story_spec (what Ads Manager shows as the ad).
|
||||
// Do NOT dump every asset_feed_spec A/B variant into the card.
|
||||
const oss = creative.object_story_spec as Record<string, unknown> | undefined;
|
||||
const link = oss?.link_data as Record<string, unknown> | undefined;
|
||||
const video = oss?.video_data as Record<string, unknown> | undefined;
|
||||
const photo = oss?.photo_data as Record<string, unknown> | undefined;
|
||||
|
||||
// Story (anuncio publicado): 1 titular + texto + descripción de enlace.
|
||||
if (link?.name) headlines.push(str(link.name));
|
||||
if (link?.message) descriptions.push(str(link.message));
|
||||
else if (link?.description) descriptions.push(str(link.description));
|
||||
if (link?.message) bodies.push(str(link.message));
|
||||
if (link?.description) descriptions.push(str(link.description));
|
||||
pushImg(link?.picture, '4:5 story', 1080, 1350);
|
||||
pushImg(link?.image_url, '4:5 story', 1080, 1350);
|
||||
pushHash(link?.image_hash);
|
||||
|
||||
if (video?.title) headlines.push(str(video.title));
|
||||
if (video?.message) descriptions.push(str(video.message));
|
||||
else if (video?.link_description) descriptions.push(str(video.link_description));
|
||||
if (video?.message) bodies.push(str(video.message));
|
||||
if (video?.link_description) descriptions.push(str(video.link_description));
|
||||
pushImg(video?.image_url, 'video_poster 9:16', 1080, 1920);
|
||||
pushHash(video?.image_hash);
|
||||
|
||||
pushImg(photo?.url, 'photo 4:5', 1080, 1350);
|
||||
pushHash(photo?.image_hash);
|
||||
|
||||
if (!headlines.length && creative.title) headlines.push(str(creative.title));
|
||||
if (!descriptions.length && creative.body) descriptions.push(str(creative.body));
|
||||
if (creative.title) headlines.push(str(creative.title));
|
||||
if (creative.body) bodies.push(str(creative.body));
|
||||
pushImg(creative.image_url, 'image 4:5', 1080, 1350);
|
||||
pushImg(creative.thumbnail_url, 'thumbnail 9:16', 1080, 1920);
|
||||
pushHash(creative.image_hash);
|
||||
|
||||
// Carousel: keep images; only first card's name as optional secondary headline.
|
||||
if (Array.isArray(link?.child_attachments)) {
|
||||
(link!.child_attachments as Record<string, unknown>[]).forEach((c, i) => {
|
||||
pushImg(c.picture || c.image_url, `carousel-${i + 1} 4:5`, 1080, 1350);
|
||||
pushHash(c.image_hash);
|
||||
if (i === 0 && c.name && !headlines.length) headlines.push(str(c.name));
|
||||
if (i === 0 && c.description && !descriptions.length) {
|
||||
descriptions.push(str(c.description));
|
||||
}
|
||||
if (c.name) headlines.push(str(c.name));
|
||||
if (c.message) bodies.push(str(c.message));
|
||||
if (c.description) descriptions.push(str(c.description));
|
||||
});
|
||||
}
|
||||
|
||||
// Dynamic creative / Advantage+: hasta 5 titles, 5 bodies, descriptions.
|
||||
const feed = creative.asset_feed_spec as Record<string, unknown> | undefined;
|
||||
if (feed) {
|
||||
// Images/videos from dynamic creative — label formats; texts only if still empty.
|
||||
for (const t of (feed.titles as { text?: string }[]) || []) {
|
||||
if (t?.text) headlines.push(str(t.text));
|
||||
}
|
||||
for (const b of (feed.bodies as { text?: string }[]) || []) {
|
||||
if (b?.text) bodies.push(str(b.text));
|
||||
}
|
||||
for (const d of (feed.descriptions as { text?: string }[]) || []) {
|
||||
if (d?.text) descriptions.push(str(d.text));
|
||||
}
|
||||
for (const img of (feed.images as Record<string, unknown>[]) || []) {
|
||||
const labels = Array.isArray(img.adlabels)
|
||||
? (img.adlabels as { name?: string }[])
|
||||
@@ -206,10 +218,9 @@ function extractCreativePack(creative: Record<string, unknown>): Extracted {
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
: '';
|
||||
const tag = labels || 'feed';
|
||||
pushImg(
|
||||
img.url || img.permalink_url,
|
||||
tag,
|
||||
labels || 'feed',
|
||||
n(img.width) || undefined,
|
||||
n(img.height) || undefined
|
||||
);
|
||||
@@ -227,16 +238,6 @@ function extractCreativePack(creative: Record<string, unknown>): Extracted {
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!headlines.length) {
|
||||
const t = (feed.titles as { text?: string }[] | undefined)?.[0]?.text;
|
||||
if (t) headlines.push(str(t));
|
||||
}
|
||||
if (!descriptions.length) {
|
||||
const b =
|
||||
(feed.bodies as { text?: string }[] | undefined)?.[0]?.text ||
|
||||
(feed.descriptions as { text?: string }[] | undefined)?.[0]?.text;
|
||||
if (b) descriptions.push(str(b));
|
||||
}
|
||||
}
|
||||
|
||||
const linkUrl =
|
||||
@@ -248,8 +249,9 @@ function extractCreativePack(creative: Record<string, unknown>): Extracted {
|
||||
const cta = str(creative.call_to_action_type) || str(ctaRaw);
|
||||
|
||||
return {
|
||||
headlines: uniq(headlines).slice(0, 3),
|
||||
descriptions: uniq(descriptions).slice(0, 3),
|
||||
headlines: uniq(headlines).slice(0, 5),
|
||||
bodies: uniq(bodies).slice(0, 5),
|
||||
descriptions: uniq(descriptions).slice(0, 5),
|
||||
assets,
|
||||
linkUrl,
|
||||
cta,
|
||||
@@ -549,6 +551,7 @@ function cardFromBundle(
|
||||
str(creative.image_url) || str(creative.thumbnail_url) || '';
|
||||
const preview = pickVerticalPreview(assets, fallback);
|
||||
const headlines = pack.headlines;
|
||||
const bodies = pack.bodies;
|
||||
const descriptions = pack.descriptions;
|
||||
return {
|
||||
platform: 'meta',
|
||||
@@ -565,8 +568,9 @@ function cardFromBundle(
|
||||
thumbnailUrl: preview,
|
||||
imageUrl: preview,
|
||||
headline: headlines.join('\n'),
|
||||
primaryText: descriptions.join('\n'),
|
||||
primaryText: bodies.join('\n') || descriptions.join('\n'),
|
||||
headlines,
|
||||
bodies,
|
||||
descriptions,
|
||||
assets,
|
||||
linkUrl: pack.linkUrl || undefined,
|
||||
@@ -751,6 +755,7 @@ export function buildGoogleCardsFromDaily(
|
||||
headline: agg.adName,
|
||||
primaryText: agg.campaignName,
|
||||
headlines: agg.adName ? [agg.adName] : [],
|
||||
bodies: [],
|
||||
descriptions: agg.campaignName ? [agg.campaignName] : [],
|
||||
assets: [],
|
||||
status: undefined,
|
||||
|
||||
Reference in New Issue
Block a user