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:
peptiderepo
2026-04-18 12:17:05 +07:00
parent dd406c5ba6
commit 1a2af595c6
6 changed files with 63 additions and 41 deletions

View File

@@ -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(

View File

@@ -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' ];

View File

@@ -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;

View File

@@ -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;
/**

View File

@@ -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' );

View File

@@ -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.'
);
/*