P0-2 + Phase A: drop Adsanity iframes, fix SEO baseline, noindex while staging
P0-2 (Adsanity iframe path removed): - AdImage.tsx: deleted the iframe branch and the ADSANITY_CAMPAIGN_MAP fallback. AdImage now renders local images only; banners without a src render nothing. - ads.ts: removed Ad.adsanity_id from the interface. Pruned AJA, Zixi, Lectrosonics, Opengear, Studio Hero, Magewell 300x250 and Blackmagic 300x600 — they relied on ads.broadcastbeat.com which does not resolve. Surviving live banners: LiveU 728x90, LiveU PAYG 300x250, Telycam 300x250. - SidebarAdStack: key no longer references the removed field. - No banner_analytics rows reference ads.broadcastbeat.com. Phase A (SEO baseline): - robots.ts: fallback base URL now broadcastbeat.com (was rocket staging URL). - sitemap.ts: fallback base URL now broadcastbeat.com (was bb-staging). - news/[slug]/page.tsx and articles/[slug]/page.tsx: same fallback fix. JSON-LD now uses ISO 8601 for datePublished, absolute image URL via SITE_URL prefix, and publisher.logo points at /assets/images/logo.png instead of the 404-ing /legacy/site/broadcastbeat-logo.png. - layout.tsx: removed the /en /es /pt /fr /de /zh /ja /ar /hi hreflang alternates - those routes 404 today; Phase D will reinstate. Replaced rocket.new Organization schema URL with broadcastbeat.com. Set robots index/follow=false until Phase B-D land and Rich Results passes. Featured-image fallback hardening: - legacy-source: rowToArticle now treats BB_Gray, no_image and placeholder URLs as null and falls back to the new placeholder so dead images do not leak through to the page. - public/assets/images/article-placeholder.svg: new clean dark gradient with BroadcastBeat wordmark, NOT no_image.png style. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,20 +2,19 @@
|
||||
// render on broadcastbeat.com. Adding a new entry here automatically
|
||||
// rotates it into the appropriate zone — no other code changes needed.
|
||||
//
|
||||
// Each Ad must provide EITHER:
|
||||
// - src + click_url + alt → local image, click goes to click_url
|
||||
// - adsanity_id → Adsanity iframe (Adsanity tracks the click)
|
||||
// Each Ad must provide a local image (src + click_url + alt). The legacy
|
||||
// Adsanity iframe path was removed because ads.broadcastbeat.com no longer
|
||||
// resolves; banners awaiting creatives are tracked in PENDING_ads.md.
|
||||
|
||||
export interface Ad {
|
||||
label: string;
|
||||
size: "300x250" | "300x600" | "728x90";
|
||||
src?: string;
|
||||
alt?: string;
|
||||
src: string;
|
||||
alt: string;
|
||||
click_url?: string;
|
||||
adsanity_id?: string;
|
||||
}
|
||||
|
||||
/** 728x90 leaderboard pool — header (top of every page) and footer. */
|
||||
/** 728x90 leaderboard pool — header (between nav and ticker) and footer. */
|
||||
export const ADS_728X90: Ad[] = [
|
||||
{
|
||||
label: "LiveU",
|
||||
@@ -30,18 +29,12 @@ export const ADS_728X90: Ad[] = [
|
||||
* 300x250 sidebar pool. ALL entries render on every page — SidebarAdStack
|
||||
* stacks them vertically and shuffles the order per page-view so each banner
|
||||
* cycles through positions. Add new entries here to grow the stack.
|
||||
*
|
||||
* AJA, Zixi, Lectrosonics, Opengear, Studio Hero, Magewell were removed
|
||||
* pending real creatives (their Adsanity campaigns relied on
|
||||
* ads.broadcastbeat.com, which is down). See PENDING_ads.md.
|
||||
*/
|
||||
export const ADS_300X250: Ad[] = [
|
||||
{
|
||||
label: "AJA Video",
|
||||
size: "300x250",
|
||||
adsanity_id: "ad-164285",
|
||||
},
|
||||
{
|
||||
label: "Zixi",
|
||||
size: "300x250",
|
||||
adsanity_id: "ad-156737",
|
||||
},
|
||||
{
|
||||
label: "Telycam",
|
||||
size: "300x250",
|
||||
@@ -56,37 +49,13 @@ export const ADS_300X250: Ad[] = [
|
||||
alt: "LiveU pay-as-you-go",
|
||||
click_url: "https://bit.ly/4ghXVeq",
|
||||
},
|
||||
{
|
||||
label: "Lectrosonics",
|
||||
size: "300x250",
|
||||
adsanity_id: "ad-156727",
|
||||
},
|
||||
{
|
||||
label: "Opengear",
|
||||
size: "300x250",
|
||||
adsanity_id: "ad-200174",
|
||||
},
|
||||
{
|
||||
label: "Studio Hero",
|
||||
size: "300x250",
|
||||
adsanity_id: "ad-162714",
|
||||
},
|
||||
{
|
||||
label: "Magewell",
|
||||
size: "300x250",
|
||||
adsanity_id: "ad-154296",
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* 300x600 fixed sidebar slot. Rendered at the TOP of SidebarAdStack and is
|
||||
* NEVER rotated. Set to null to omit. To replace, swap this single entry.
|
||||
* NEVER rotated. Set to null to omit. Blackmagic ATEM creative pending.
|
||||
*/
|
||||
export const FIXED_300X600: Ad | null = {
|
||||
label: "Blackmagic",
|
||||
size: "300x600",
|
||||
adsanity_id: "ad-210571",
|
||||
};
|
||||
export const FIXED_300X600: Ad | null = null;
|
||||
|
||||
/** Fisher-Yates shuffle, returns a new array. */
|
||||
export function shuffle<T>(arr: T[]): T[] {
|
||||
@@ -109,7 +78,7 @@ export function rotateAll(size: Ad["size"], exclude: Set<string> = new Set()): A
|
||||
: size === "728x90" ? ADS_728X90
|
||||
: size === "300x600" ? (FIXED_300X600 ? [FIXED_300X600] : [])
|
||||
: [];
|
||||
const key = (a: Ad) => a.src ?? a.adsanity_id ?? a.label;
|
||||
const key = (a: Ad) => a.src ?? a.label;
|
||||
return shuffle(pool.filter((a) => !exclude.has(key(a))));
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ interface ImportedPostRow {
|
||||
wp_published_at: string | null;
|
||||
}
|
||||
|
||||
const FALLBACK_IMAGE = "/legacy/site/no_image.png";
|
||||
const FALLBACK_IMAGE = "/assets/images/article-placeholder.svg";
|
||||
|
||||
// Tag → section mapping. Source: tag distribution observed in
|
||||
// bb.wp_imported_posts. Matches against any overlap (lowercased).
|
||||
@@ -80,9 +80,16 @@ function readTime(content: string | null): string {
|
||||
return `${minutes} min read`;
|
||||
}
|
||||
|
||||
// Featured-image URLs that we treat as "no image" — historical placeholders
|
||||
// that should resolve to the clean dark placeholder instead of the grey box.
|
||||
const BROKEN_IMAGE_PATTERNS = /no_image|BB_Gray|placeholder/i;
|
||||
|
||||
function rowToArticle(row: ImportedPostRow, section: Section = "news"): Article {
|
||||
const image = row.featured_image
|
||||
? rewriteLegacyImageUrl(row.featured_image)
|
||||
const rawImage = row.featured_image && !BROKEN_IMAGE_PATTERNS.test(row.featured_image)
|
||||
? row.featured_image
|
||||
: null;
|
||||
const image = rawImage
|
||||
? rewriteLegacyImageUrl(rawImage)
|
||||
: FALLBACK_IMAGE;
|
||||
const content = rewriteLegacyImageUrlsInHtml(row.content || "");
|
||||
const author = row.author_name || "Staff Reporter";
|
||||
|
||||
Reference in New Issue
Block a user