fix: attach images immediately + upgrade image prompts to v2
Two fixes: 1. Image attachment: moved set_post_thumbnail() and Image B post meta into generate_and_attach_images() so they persist immediately after each image generates. Previously the caller set them after both images completed — if the process timed out (common with GPT-5 Image at ~30s/image), the featured image was never attached and Image B was never attempted. 2. Image prompts: replaced FLUX.1-optimized rewriter system prompt and 90s infomercial style suffix with prompts optimized for Gemini Flash Image and GPT-5 Image. New style: premium scientific lifestyle photography with golds/teals color grading. Includes one-time migration to update stored style suffix. Also logs when Image B is skipped due to missing source data (previously silent), helping diagnose why some posts only get one image. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -152,18 +152,10 @@ class PRAutoBlogger_Executor {
|
||||
is_array( $source_ids ) ? array_map( 'absint', $source_ids ) : []
|
||||
);
|
||||
|
||||
// The pipeline now sets featured image and Image B meta
|
||||
// internally, immediately after each image generates.
|
||||
$result = $pipeline->generate_and_attach_images( $post_id, $article_data, $source_data );
|
||||
|
||||
// Set featured image if Image A was generated.
|
||||
if ( ! empty( $result['image_a_id'] ) ) {
|
||||
set_post_thumbnail( $post_id, $result['image_a_id'] );
|
||||
}
|
||||
|
||||
// Store Image B in post meta if generated.
|
||||
if ( ! empty( $result['image_b_id'] ) ) {
|
||||
update_post_meta( $post_id, '_prautoblogger_image_b_id', $result['image_b_id'] );
|
||||
}
|
||||
|
||||
wp_send_json_success( $result );
|
||||
} catch ( \Throwable $e ) {
|
||||
PRAutoBlogger_Logger::instance()->error(
|
||||
|
||||
@@ -123,6 +123,19 @@ class PRAutoBlogger {
|
||||
update_option( 'prautoblogger_migrated_gemini_flash_lite', '1' );
|
||||
}
|
||||
|
||||
// One-time migration: replace 90s infomercial style suffix with premium
|
||||
// science editorial style. The old pastiche rendered poorly on Gemini/GPT-5
|
||||
// Image models. Only overwrite if the stored value IS the old default — if
|
||||
// the user customized it, respect their choice.
|
||||
if ( ! get_option( 'prautoblogger_migrated_style_suffix_v2' ) ) {
|
||||
$old_infomercial = 'Style: a screengrab from a 1995 late-night cable TV infomercial';
|
||||
$current = get_option( 'prautoblogger_image_style_suffix', '' );
|
||||
if ( '' === $current || false !== strpos( $current, $old_infomercial ) ) {
|
||||
update_option( 'prautoblogger_image_style_suffix', PRAUTOBLOGGER_DEFAULT_IMAGE_STYLE_SUFFIX );
|
||||
}
|
||||
update_option( 'prautoblogger_migrated_style_suffix_v2', '1' );
|
||||
}
|
||||
|
||||
// One-time migration: re-wrap existing encrypted values with "enc:" prefix.
|
||||
if ( ! get_option( 'prautoblogger_migrated_enc_prefix' ) ) {
|
||||
$enc_options = [ 'prautoblogger_openrouter_api_key', 'prautoblogger_ga4_credentials_json' ];
|
||||
|
||||
@@ -138,6 +138,15 @@ class PRAutoBlogger_Image_Pipeline {
|
||||
$image_a_result = $this->generate_image_a( $post_id, $article_data );
|
||||
if ( ! is_wp_error( $image_a_result ) && isset( $image_a_result['attachment_id'] ) ) {
|
||||
$result['image_a_id'] = $image_a_result['attachment_id'];
|
||||
|
||||
// Set featured image immediately so it persists even if the
|
||||
// process times out before Image B finishes or before the
|
||||
// caller gets to handle the return value.
|
||||
set_post_thumbnail( $post_id, $image_a_result['attachment_id'] );
|
||||
PRAutoBlogger_Logger::instance()->info(
|
||||
sprintf( 'Set featured image (attachment %d) for post %d', $image_a_result['attachment_id'], $post_id ),
|
||||
'image_pipeline'
|
||||
);
|
||||
} else {
|
||||
$result['errors'][] = is_wp_error( $image_a_result )
|
||||
? $image_a_result->get_error_message()
|
||||
@@ -152,6 +161,14 @@ class PRAutoBlogger_Image_Pipeline {
|
||||
$image_b_result = $this->generate_image_b( $post_id, $source_data );
|
||||
if ( ! is_wp_error( $image_b_result ) && isset( $image_b_result['attachment_id'] ) ) {
|
||||
$result['image_b_id'] = $image_b_result['attachment_id'];
|
||||
|
||||
// Store Image B reference immediately for the same
|
||||
// timeout-resilience reason as Image A above.
|
||||
update_post_meta( $post_id, '_prautoblogger_image_b_id', $image_b_result['attachment_id'] );
|
||||
PRAutoBlogger_Logger::instance()->info(
|
||||
sprintf( 'Stored Image B (attachment %d) for post %d', $image_b_result['attachment_id'], $post_id ),
|
||||
'image_pipeline'
|
||||
);
|
||||
} else {
|
||||
$result['errors'][] = is_wp_error( $image_b_result )
|
||||
? $image_b_result->get_error_message()
|
||||
@@ -160,6 +177,11 @@ class PRAutoBlogger_Image_Pipeline {
|
||||
if ( ! is_wp_error( $image_b_result ) ) {
|
||||
$result['cost_usd'] += $image_b_result['cost_usd'] ?? 0.0;
|
||||
}
|
||||
} else {
|
||||
PRAutoBlogger_Logger::instance()->info(
|
||||
sprintf( 'Image B skipped for post %d: no source data available.', $post_id ),
|
||||
'image_pipeline'
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
@@ -28,17 +28,22 @@ class PRAutoBlogger_Image_Prompt_Builder {
|
||||
* code review, not an admin-panel click.
|
||||
*/
|
||||
private const REWRITER_SYSTEM_PROMPT = <<<'PROMPT'
|
||||
You are an image-prompt specialist for FLUX.1, a text-to-image model.
|
||||
You are an image-prompt specialist for premium AI image models (Gemini Flash Image, GPT-5 Image).
|
||||
|
||||
Given an article title and summary, output ONE short paragraph (2-4 sentences, under 60 words) describing a concrete visual scene that would make a compelling featured image for the article.
|
||||
Given an article title and summary about peptides, supplements, or biohacking, write ONE vivid scene description (2-3 sentences, under 80 words) for a compelling featured image. Emphasize:
|
||||
- OBJECTS and MATERIALS: lab glassware, peptide vials, molecular structures, syringes, supplement bottles, biometric devices, microscopes, fitness equipment, test tubes
|
||||
- LIGHTING and DEPTH: bright directional studio lighting, selective focus, rich warm/cool color contrasts
|
||||
- COMPOSITION: dynamic framing with clear focal point, professional product photography perspective, sense of scale and precision
|
||||
- MOOD: scientific credibility paired with aspirational wellness — premium feel, approachable, engaging
|
||||
|
||||
Rules:
|
||||
- Describe OBJECTS, SETTING, LIGHTING, COMPOSITION, and MOOD — not abstract concepts.
|
||||
- Never include text, words, labels, logos, or watermarks in the scene.
|
||||
- Never describe people's faces in detail (FLUX.1 struggles with faces). Use silhouettes, hands, or over-the-shoulder framings instead.
|
||||
- Focus on the article's core SUBJECT as a physical thing: lab equipment, supplements, syringes, molecules, gym settings, packaging, etc.
|
||||
- Do NOT include any style direction — that is appended separately.
|
||||
- Output ONLY the scene description. No preamble, no explanation, no quotes.
|
||||
Avoid:
|
||||
- Abstract concepts; show concrete objects only
|
||||
- Text, logos, watermarks, or readable labels
|
||||
- Detailed human faces (use hands, torso, silhouettes, or exclude people)
|
||||
- Multiple competing visual elements; keep the subject dominant
|
||||
- Generic stock-photo composition
|
||||
|
||||
Output ONLY the scene description — no preamble, explanation, or quotes. Do NOT include any style direction; that is appended separately.
|
||||
PROMPT;
|
||||
|
||||
/**
|
||||
|
||||
@@ -138,24 +138,12 @@ class PRAutoBlogger_Post_Assembler {
|
||||
$source_data = ( new PRAutoBlogger_Source_Collector() )->get_source_data_for_image( $idea->get_source_ids() );
|
||||
|
||||
try {
|
||||
// The pipeline now sets featured image and Image B meta
|
||||
// internally, immediately after each image generates. This
|
||||
// ensures attachment persists even if the process times out
|
||||
// before this method returns.
|
||||
$result = ( new PRAutoBlogger_Image_Pipeline() )->generate_and_attach_images( $post_id, $post_data, $source_data );
|
||||
|
||||
if ( isset( $result['image_a_id'] ) ) {
|
||||
set_post_thumbnail( $post_id, $result['image_a_id'] );
|
||||
PRAutoBlogger_Logger::instance()->info(
|
||||
sprintf( 'Set featured image (attachment %d) for post %d', $result['image_a_id'], $post_id ),
|
||||
'publisher'
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $result['image_b_id'] ) ) {
|
||||
update_post_meta( $post_id, '_prautoblogger_image_b_id', $result['image_b_id'] );
|
||||
PRAutoBlogger_Logger::instance()->info(
|
||||
sprintf( 'Stored Image B (attachment %d) for post %d', $result['image_b_id'], $post_id ),
|
||||
'publisher'
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! empty( $result['errors'] ) ) {
|
||||
foreach ( $result['errors'] as $error ) {
|
||||
PRAutoBlogger_Logger::instance()->warning( 'Image warning for post ' . $post_id . ': ' . $error, 'publisher' );
|
||||
|
||||
@@ -8,7 +8,7 @@ declare(strict_types=1);
|
||||
* Plugin Name: PRAutoBlogger
|
||||
* Plugin URI: https://peptiderepo.com/prautoblogger
|
||||
* Description: Monitors social media for trending topics, generates SEO-friendly blog posts using AI, and publishes them on a daily schedule with full cost tracking and self-improvement metrics.
|
||||
* Version: 0.2.7
|
||||
* Version: 0.2.8
|
||||
* Requires at least: 6.0
|
||||
* Requires PHP: 7.4
|
||||
* Author: PeptideRepo
|
||||
@@ -35,7 +35,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
| and limits without magic strings.
|
||||
*/
|
||||
|
||||
define( 'PRAUTOBLOGGER_VERSION', '0.2.7' );
|
||||
define( 'PRAUTOBLOGGER_VERSION', '0.2.8' );
|
||||
define( 'PRAUTOBLOGGER_DB_VERSION', '1.1.0' );
|
||||
define( 'PRAUTOBLOGGER_PLUGIN_FILE', __FILE__ );
|
||||
define( 'PRAUTOBLOGGER_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
||||
@@ -59,12 +59,14 @@ define( 'PRAUTOBLOGGER_DEFAULT_EDITOR_MODEL', 'google/gemini-2.5-flash-lite' );
|
||||
define( 'PRAUTOBLOGGER_DEFAULT_IMAGE_PROVIDER', 'openrouter' );
|
||||
define( 'PRAUTOBLOGGER_DEFAULT_IMAGE_MODEL', 'google/gemini-2.5-flash-image' );
|
||||
|
||||
// CEO-approved image style suffix (decision locked 2026-04-15). Users may
|
||||
// override via Settings → Images → Style Suffix; this constant is the
|
||||
// fallback the setting reads on first install.
|
||||
// Default style suffix appended to every image-gen prompt to create a
|
||||
// cohesive visual brand across all article images. Updated 2026-04-18
|
||||
// after switching from FLUX.1 to Gemini/GPT-5 Image models — the old
|
||||
// 90s infomercial pastiche rendered poorly on the new models. Users may
|
||||
// override via Settings → Images → Style Suffix.
|
||||
define(
|
||||
'PRAUTOBLOGGER_DEFAULT_IMAGE_STYLE_SUFFIX',
|
||||
'Style: a screengrab from a 1995 late-night cable TV infomercial, oversaturated warm colors, VHS tape noise and scan lines, a bright yellow starburst graphic in the top right corner reading NEW, cheap studio lighting, dated 4:3 television framing, deadpan earnest expression, faint lens distortion, tacky low-budget commercial aesthetic.'
|
||||
'Style: premium scientific lifestyle photography, professional laboratory setting with warm studio lighting, shallow depth of field, color-graded for wellness branding in rich golds and deep teals, high-end editorial quality.'
|
||||
);
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user