Plugin Directory

Changeset 3460335


Ignore:
Timestamp:
02/12/2026 08:46:29 PM (6 weeks ago)
Author:
andrejdivi
Message:

Update to version 1.6.1 from GitHub

Location:
alt-text-imagerr-ai
Files:
10 edited
1 copied

Legend:

Unmodified
Added
Removed
  • alt-text-imagerr-ai/tags/1.6.1/assets/imagerr-settings.js

    r3434623 r3460335  
    1515    }
    1616
     17    var progressIntervalId = null;
     18
     19    function fetchBulkStatus() {
     20        $.get({
     21            url: imagerr_vars.rest_url + '/bulk-status',
     22            data: {
     23                _wpnonce: imagerr_vars.nonce,
     24                _t: Date.now()
     25            },
     26            success: function(response) {
     27                // Restore status text in case it was set to "Reconnecting…" by a previous error
     28                if (response.processed !== response.total && $('#generate-meta-bulk-status').length) {
     29                    $('#generate-meta-bulk-status').text(imagerr_vars.i18n.status_generating);
     30                }
     31                var progressText = response.processed + '/' + response.total + ' ' + imagerr_vars.i18n.images_processed;
     32                $('#generate-meta-bulk-progress-text').text(progressText);
     33
     34                // Calculate percentage for progress bar and ensure it's a valid number
     35                var percentage = 0;
     36                if (response.total > 0) {
     37                    percentage = Math.min(100, Math.max(0, (response.processed / response.total) * 100));
     38                }
     39                $('#generate-meta-bulk-progress').val(percentage);
     40
     41                // Update credits display if available
     42                if (response.credits !== undefined) {
     43                    updateCreditsWithAnimation(response.credits);
     44                }
     45
     46                if (response.processed === response.total) {
     47                    if (progressIntervalId) {
     48                        clearInterval(progressIntervalId);
     49                        progressIntervalId = null;
     50                    }
     51                    $('#generate-meta-bulk-progress').val(100);
     52                    $('#generate-meta-bulk-progress-text').text(imagerr_vars.i18n.all_images_processed);
     53                    resetButtonAndStatus();
     54                    fetchErrorImages();
     55                }
     56            },
     57            error: function() {
     58                // Don't clear interval; show reconnecting so user knows UI is temporarily stale
     59                if (progressIntervalId && $('#generate-meta-bulk-status').length) {
     60                    $('#generate-meta-bulk-status').text(imagerr_vars.i18n.status_reconnecting || 'Reconnecting…');
     61                }
     62            }
     63        });
     64    }
     65
    1766    function startProgressInterval() {
    18         var intervalId = setInterval(function() {
    19             $.get({
    20                 url: imagerr_vars.rest_url + '/bulk-status',
    21                 data: {
    22                     _wpnonce: imagerr_vars.nonce
    23                 },
    24                 success: function(response) {
    25                     var progressText = response.processed + '/' + response.total + ' ' + imagerr_vars.i18n.images_processed;
    26                     $('#generate-meta-bulk-progress-text').text(progressText);
    27                    
    28                     // Calculate percentage for progress bar and ensure it's a valid number
    29                     var percentage = 0;
    30                     if (response.total > 0) {
    31                         percentage = Math.min(100, Math.max(0, (response.processed / response.total) * 100));
    32                     }
    33                     $('#generate-meta-bulk-progress').val(percentage);
    34                    
    35                     // Update credits display if available
    36                     if (response.credits !== undefined) {
    37                         updateCreditsWithAnimation(response.credits);
    38                     }
    39                    
    40                     if (response.processed === response.total) {
    41                         clearInterval(intervalId);
    42                         // Keep the final progress values
    43                         $('#generate-meta-bulk-progress').val(100);
    44                         $('#generate-meta-bulk-progress-text').text(imagerr_vars.i18n.all_images_processed);
    45                         resetButtonAndStatus();
    46                         // Get error images after completion
    47                         fetchErrorImages();
    48                     }
    49                 }
    50             });
    51         }, 2000);
     67        if (progressIntervalId) {
     68            clearInterval(progressIntervalId);
     69        }
     70        progressIntervalId = setInterval(fetchBulkStatus, 2000);
    5271    }
    5372
     
    165184    fetchErrorImages();
    166185
     186    // When tab becomes visible, fetch status immediately so progress bar catches up
     187    document.addEventListener('visibilitychange', function() {
     188        if (document.visibilityState === 'visible' && progressIntervalId !== null) {
     189            fetchBulkStatus();
     190        }
     191    });
     192
    167193    // Check if we're in a completed state
    168194    if (imagerr_vars.is_generating) {
  • alt-text-imagerr-ai/tags/1.6.1/imagerr.php

    r3438971 r3460335  
    33 * Plugin Name: AI Image Alt Text Generator – Imagerr AI
    44 * Description: Generate alt text, titles, descriptions, and captions for your images automatically with AI.
    5  * Version: 1.6
     5 * Version: 1.6.1
    66 * Text Domain: alt-text-imagerr-ai
    77 * Domain Path: /languages
     
    346346        }
    347347
    348         // Check if $meta_bulk_async is running.
    349         $is_generating = $this->meta_bulk_async->is_processing();
     348        // Check if $meta_bulk_async is running or queue has items (e.g. between batches).
     349        $is_generating = $this->meta_bulk_async->is_processing() || $this->meta_bulk_async->is_queued();
    350350        $progress = 0;
    351351        $progress_text = '--';
     
    385385            wp_enqueue_script( 'imagerr-settings-script', plugin_dir_url( __FILE__ ) . 'assets/imagerr-settings.js', array( 'jquery' ), IMAGERR_VERSION, true );
    386386
    387             // Check if $meta_bulk_async is running.
    388             $is_generating = $this->meta_bulk_async->is_processing();
     387            // Check if $meta_bulk_async is running or queue has items (e.g. between batches).
     388            $is_generating = $this->meta_bulk_async->is_processing() || $this->meta_bulk_async->is_queued();
    389389
    390390            // Check if we're in selected images mode
     
    407407                        'status_generating'   => esc_html__( '🏃‍♂️‍➡️ Generating...', 'alt-text-imagerr-ai' ),
    408408                        'status_stopping_generation' => esc_html__( '🚫 Stopping generation...', 'alt-text-imagerr-ai' ),
     409                        'status_reconnecting' => esc_html__( 'Reconnecting…', 'alt-text-imagerr-ai' ),
    409410                        'image_updated' => esc_html__( '✅ Image updated', 'alt-text-imagerr-ai' ),
    410411                        'images_processed' => esc_html__( 'images processed', 'alt-text-imagerr-ai' ),
     
    703704        $credits = $this->meta->get_credits();
    704705        $status['credits'] = $credits;
    705         return new \WP_REST_Response( $status, 200 );
     706
     707        // If queue has work but process is not running (e.g. dispatch failed), try to wake it up (throttled).
     708        if ( $this->meta_bulk_async->is_queued() && ! $this->meta_bulk_async->is_processing() ) {
     709            $wakeup_transient = 'imagerr_meta_bulk_wakeup_attempted';
     710            if ( ! get_site_transient( $wakeup_transient ) ) {
     711                set_site_transient( $wakeup_transient, true, 60 );
     712                $this->meta_bulk_async->log_message( 'Dispatched from bulk-status (status check wake-up)' );
     713                $this->meta_bulk_async->dispatch();
     714            }
     715        }
     716
     717        $response = new \WP_REST_Response( $status, 200 );
     718        $response->header( 'Cache-Control', 'no-store, no-cache, must-revalidate' );
     719        $response->header( 'Pragma', 'no-cache' );
     720        return $response;
    706721    }
    707722
  • alt-text-imagerr-ai/tags/1.6.1/readme.txt

    r3438971 r3460335  
    55Requires PHP: 5.2
    66Requires at least: 4.6
    7 Stable tag: 1.6
     7Stable tag: 1.6.1
    88Tested up to: 6.9
    99License: GPLv2 or later
     
    7171
    7272== Changelog ==
     73= 1.6.1 =
     74* Added extra debug logging to the background process for easier troubleshooting
     75* Refined bulk generation and background processing for better reliability
     76
    7377= 1.6 =
    7478* Added support for palette type images
  • alt-text-imagerr-ai/tags/1.6.1/src/Async/BackgroundProcess.php

    r3438971 r3460335  
    105105
    106106    /**
     107     * Log a message to the debug file (public wrapper for callers outside the process).
     108     *
     109     * @param string $message The message to log.
     110     * @return void
     111     */
     112    public function log_message( $message ) {
     113        $this->log( $message );
     114    }
     115
     116    /**
    107117     * Log a message to the debug file.
    108118     *
     
    113123        // Only log if debug is enabled or if debug logs option is enabled
    114124        $debug_enabled = ( defined( 'IMAGERR_DEBUG' ) && IMAGERR_DEBUG ) || get_option( 'imagerr_enable_debug_logs', false );
    115        
     125
    116126        if ( $debug_enabled ) {
    117127            // Rotate log if it exceeds size limit
    118128            \Imagerr::rotate_log_if_needed();
    119            
     129
    120130            $log_file = \Imagerr::get_debug_log_path();
    121131            $timestamp = date( 'Y-m-d H:i:s' );
     
    140150        $this->schedule_event();
    141151
     152        // Get URL and log it for debugging
     153        $url = add_query_arg( $this->get_query_args(), $this->get_query_url() );
     154        $this->log( sprintf( 'Dispatching to URL: %s', $url ) );
     155
     156        // Check if URL is accessible (common issue with localhost/dev sites)
     157        $parsed_url = parse_url( $url );
     158        if ( isset( $parsed_url['host'] ) && in_array( $parsed_url['host'], array( 'localhost', '127.0.0.1', '::1' ), true ) ) {
     159            $this->log( 'WARNING: Using localhost URL - async requests may fail. Cron healthcheck will be used as fallback.' );
     160        }
     161
    142162        // Perform remote post.
    143163        $result = parent::dispatch();
    144        
     164
    145165        if ( is_wp_error( $result ) ) {
    146             $this->log( sprintf( 'Dispatch failed: %s', $result->get_error_message() ) );
     166            $this->log( sprintf( 'Dispatch failed with WP_Error: %s (code: %s)', $result->get_error_message(), $result->get_error_code() ) );
     167            $this->schedule_fallback_healthcheck();
    147168        } elseif ( false === $result ) {
    148169            $this->log( 'Dispatch returned false' );
     170            $this->schedule_fallback_healthcheck();
    149171        } else {
    150             $this->log( 'Dispatch successful' );
    151         }
    152        
     172            // Check response code even for non-blocking requests
     173            $response_code = wp_remote_retrieve_response_code( $result );
     174            if ( $response_code ) {
     175                $this->log( sprintf( 'Dispatch successful - Response code: %d', $response_code ) );
     176            } else {
     177                $this->log( 'Dispatch successful (non-blocking request - response code not available)' );
     178            }
     179            $response_body = wp_remote_retrieve_body( $result );
     180            $this->log( sprintf( 'Dispatch response body: %s', $response_body !== '' ? $response_body : '(empty)' ) );
     181            $this->log( 'Note: If processing does not start, cron healthcheck will handle it.' );
     182        }
     183
    153184        return $result;
    154185    }
     
    447478
    448479        $lock_acquired = set_site_transient( $this->identifier . '_process_lock', microtime(), $lock_duration );
    449        
     480
    450481        if ( ! $lock_acquired ) {
    451482            $this->log( sprintf( 'WARNING: Failed to acquire process lock (duration: %d seconds)', $lock_duration ) );
     
    464495    protected function unlock_process() {
    465496        $deleted = delete_site_transient( $this->identifier . '_process_lock' );
    466        
     497
    467498        if ( ! $deleted ) {
    468499            $this->log( 'WARNING: Failed to release process lock' );
     
    550581                        $batch->key  = $item->{$column};
    551582                        $batch->data = static::maybe_unserialize( $item->{$value_column}, $allowed_classes );
    552                        
     583
    553584                        if ( false === $batch->data && is_serialized( $item->{$value_column} ) ) {
    554585                            $this->log( sprintf( 'WARNING: Failed to unserialize batch data for key: %s', $batch->key ) );
    555586                            return null;
    556587                        }
    557                        
     588
    558589                        return $batch;
    559590                    } catch ( \Exception $e ) {
     
    564595                $items
    565596            );
    566            
     597
    567598            // Filter out null values
    568599            $batches = array_filter( $batches );
     
    609640
    610641            $batch = $this->get_batch();
    611            
     642
    612643            if ( empty( $batch ) || empty( $batch->data ) ) {
    613644                $this->log( 'No batch found or empty batch, stopping' );
    614645                break;
    615646            }
    616            
     647
    617648            $this->log( sprintf( 'Processing batch %s with %d items', $batch->key, count( $batch->data ) ) );
    618649
     
    699730
    700731        if ( $current_memory >= $memory_limit ) {
    701             $this->log( sprintf( 
    702                 'Memory limit exceeded: %s / %s (90%% of limit)', 
     732            $this->log( sprintf(
     733                'Memory limit exceeded: %s / %s (90%% of limit)',
    703734                size_format( $current_memory ),
    704735                size_format( $this->get_memory_limit() )
     
    746777
    747778        if ( time() >= $finish ) {
    748             $this->log( sprintf( 
    749                 'Time limit exceeded: %d seconds elapsed (limit: %d seconds)', 
     779            $this->log( sprintf(
     780                'Time limit exceeded: %d seconds elapsed (limit: %d seconds)',
    750781                $elapsed,
    751782                $time_limit
     
    828859
    829860    /**
     861     * Schedule a one-off healthcheck soon when immediate dispatch failed.
     862     * Ensures the queue resumes within ~30s instead of waiting for the next recurring cron.
     863     */
     864    protected function schedule_fallback_healthcheck() {
     865        $delay_seconds = (int) apply_filters( $this->identifier . '_fallback_healthcheck_delay', 30 );
     866        $delay_seconds = max( 10, min( 120, $delay_seconds ) );
     867        $when         = time() + $delay_seconds;
     868        $scheduled    = wp_schedule_single_event( $when, $this->cron_hook_identifier );
     869        if ( false === $scheduled || is_wp_error( $scheduled ) ) {
     870            $this->log( sprintf(
     871                'Fallback healthcheck schedule failed (in %d s). Recurring cron will retry.',
     872                $delay_seconds
     873            ) );
     874        } else {
     875            $this->log( sprintf(
     876                'Fallback healthcheck scheduled in %d seconds (hook: %s)',
     877                $delay_seconds,
     878                $this->cron_hook_identifier
     879            ) );
     880        }
     881    }
     882
     883    /**
    830884     * Handle cron healthcheck event.
    831885     *
     
    835889    public function handle_cron_healthcheck() {
    836890        $this->log( 'Cron healthcheck triggered' );
    837        
     891
    838892        if ( $this->is_processing() ) {
    839893            $lock_time = get_site_transient( $this->identifier . '_process_lock' );
    840894            $lock_age = $lock_time ? time() - (int) $lock_time : 0;
    841            
    842             $this->log( sprintf( 
    843                 'Process already running (lock age: %d seconds), skipping', 
     895
     896            $this->log( sprintf(
     897                'Process already running (lock age: %d seconds), skipping',
    844898                $lock_age
    845899            ) );
     
    861915     */
    862916    protected function schedule_event() {
    863         if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
    864             wp_schedule_event( time() + ( $this->get_cron_interval() * MINUTE_IN_SECONDS ), $this->cron_interval_identifier, $this->cron_hook_identifier );
     917        $next_scheduled = wp_next_scheduled( $this->cron_hook_identifier );
     918        $interval      = $this->get_cron_interval();
     919
     920        if ( $next_scheduled ) {
     921            $this->log( sprintf(
     922                'Cron event already scheduled (hook: %s, interval: %d min). Next run: %s',
     923                $this->cron_hook_identifier,
     924                $interval,
     925                gmdate( 'Y-m-d H:i:s', $next_scheduled )
     926            ) );
     927            return;
     928        }
     929
     930        $first_run = time() + ( $interval * MINUTE_IN_SECONDS );
     931        $scheduled = wp_schedule_event( $first_run, $this->cron_interval_identifier, $this->cron_hook_identifier );
     932
     933        if ( false === $scheduled || is_wp_error( $scheduled ) ) {
     934            $this->log( sprintf(
     935                'Cron schedule FAILED (hook: %s, interval: %d min). wp_schedule_event returned: %s',
     936                $this->cron_hook_identifier,
     937                $interval,
     938                is_wp_error( $scheduled ) ? $scheduled->get_error_message() : 'false'
     939            ) );
     940        } else {
     941            $this->log( sprintf(
     942                'Cron event scheduled successfully (hook: %s, interval: %d min). First run: %s',
     943                $this->cron_hook_identifier,
     944                $interval,
     945                gmdate( 'Y-m-d H:i:s', $first_run )
     946            ) );
    865947        }
    866948    }
     
    874956        if ( $timestamp ) {
    875957            wp_unschedule_event( $timestamp, $this->cron_hook_identifier );
     958            $this->log( sprintf(
     959                'Cron event cleared (hook: %s, was scheduled for: %s)',
     960                $this->cron_hook_identifier,
     961                gmdate( 'Y-m-d H:i:s', $timestamp )
     962            ) );
     963        } else {
     964            $this->log( sprintf(
     965                'Cron event clear: nothing scheduled (hook: %s)',
     966                $this->cron_hook_identifier
     967            ) );
    876968        }
    877969    }
  • alt-text-imagerr-ai/tags/1.6.1/src/MetaBulkAsync.php

    r3434623 r3460335  
    3030     */
    3131    protected $action = 'generate_meta_bulk';
     32
     33    /**
     34     * Cron healthcheck interval in minutes.
     35     * Shorter than default (5) so fallback cron resumes queue sooner if dispatch fails.
     36     *
     37     * @var int
     38     */
     39    protected $cron_interval = 1;
    3240
    3341    /**
  • alt-text-imagerr-ai/trunk/assets/imagerr-settings.js

    r3434623 r3460335  
    1515    }
    1616
     17    var progressIntervalId = null;
     18
     19    function fetchBulkStatus() {
     20        $.get({
     21            url: imagerr_vars.rest_url + '/bulk-status',
     22            data: {
     23                _wpnonce: imagerr_vars.nonce,
     24                _t: Date.now()
     25            },
     26            success: function(response) {
     27                // Restore status text in case it was set to "Reconnecting…" by a previous error
     28                if (response.processed !== response.total && $('#generate-meta-bulk-status').length) {
     29                    $('#generate-meta-bulk-status').text(imagerr_vars.i18n.status_generating);
     30                }
     31                var progressText = response.processed + '/' + response.total + ' ' + imagerr_vars.i18n.images_processed;
     32                $('#generate-meta-bulk-progress-text').text(progressText);
     33
     34                // Calculate percentage for progress bar and ensure it's a valid number
     35                var percentage = 0;
     36                if (response.total > 0) {
     37                    percentage = Math.min(100, Math.max(0, (response.processed / response.total) * 100));
     38                }
     39                $('#generate-meta-bulk-progress').val(percentage);
     40
     41                // Update credits display if available
     42                if (response.credits !== undefined) {
     43                    updateCreditsWithAnimation(response.credits);
     44                }
     45
     46                if (response.processed === response.total) {
     47                    if (progressIntervalId) {
     48                        clearInterval(progressIntervalId);
     49                        progressIntervalId = null;
     50                    }
     51                    $('#generate-meta-bulk-progress').val(100);
     52                    $('#generate-meta-bulk-progress-text').text(imagerr_vars.i18n.all_images_processed);
     53                    resetButtonAndStatus();
     54                    fetchErrorImages();
     55                }
     56            },
     57            error: function() {
     58                // Don't clear interval; show reconnecting so user knows UI is temporarily stale
     59                if (progressIntervalId && $('#generate-meta-bulk-status').length) {
     60                    $('#generate-meta-bulk-status').text(imagerr_vars.i18n.status_reconnecting || 'Reconnecting…');
     61                }
     62            }
     63        });
     64    }
     65
    1766    function startProgressInterval() {
    18         var intervalId = setInterval(function() {
    19             $.get({
    20                 url: imagerr_vars.rest_url + '/bulk-status',
    21                 data: {
    22                     _wpnonce: imagerr_vars.nonce
    23                 },
    24                 success: function(response) {
    25                     var progressText = response.processed + '/' + response.total + ' ' + imagerr_vars.i18n.images_processed;
    26                     $('#generate-meta-bulk-progress-text').text(progressText);
    27                    
    28                     // Calculate percentage for progress bar and ensure it's a valid number
    29                     var percentage = 0;
    30                     if (response.total > 0) {
    31                         percentage = Math.min(100, Math.max(0, (response.processed / response.total) * 100));
    32                     }
    33                     $('#generate-meta-bulk-progress').val(percentage);
    34                    
    35                     // Update credits display if available
    36                     if (response.credits !== undefined) {
    37                         updateCreditsWithAnimation(response.credits);
    38                     }
    39                    
    40                     if (response.processed === response.total) {
    41                         clearInterval(intervalId);
    42                         // Keep the final progress values
    43                         $('#generate-meta-bulk-progress').val(100);
    44                         $('#generate-meta-bulk-progress-text').text(imagerr_vars.i18n.all_images_processed);
    45                         resetButtonAndStatus();
    46                         // Get error images after completion
    47                         fetchErrorImages();
    48                     }
    49                 }
    50             });
    51         }, 2000);
     67        if (progressIntervalId) {
     68            clearInterval(progressIntervalId);
     69        }
     70        progressIntervalId = setInterval(fetchBulkStatus, 2000);
    5271    }
    5372
     
    165184    fetchErrorImages();
    166185
     186    // When tab becomes visible, fetch status immediately so progress bar catches up
     187    document.addEventListener('visibilitychange', function() {
     188        if (document.visibilityState === 'visible' && progressIntervalId !== null) {
     189            fetchBulkStatus();
     190        }
     191    });
     192
    167193    // Check if we're in a completed state
    168194    if (imagerr_vars.is_generating) {
  • alt-text-imagerr-ai/trunk/imagerr.php

    r3438971 r3460335  
    33 * Plugin Name: AI Image Alt Text Generator – Imagerr AI
    44 * Description: Generate alt text, titles, descriptions, and captions for your images automatically with AI.
    5  * Version: 1.6
     5 * Version: 1.6.1
    66 * Text Domain: alt-text-imagerr-ai
    77 * Domain Path: /languages
     
    346346        }
    347347
    348         // Check if $meta_bulk_async is running.
    349         $is_generating = $this->meta_bulk_async->is_processing();
     348        // Check if $meta_bulk_async is running or queue has items (e.g. between batches).
     349        $is_generating = $this->meta_bulk_async->is_processing() || $this->meta_bulk_async->is_queued();
    350350        $progress = 0;
    351351        $progress_text = '--';
     
    385385            wp_enqueue_script( 'imagerr-settings-script', plugin_dir_url( __FILE__ ) . 'assets/imagerr-settings.js', array( 'jquery' ), IMAGERR_VERSION, true );
    386386
    387             // Check if $meta_bulk_async is running.
    388             $is_generating = $this->meta_bulk_async->is_processing();
     387            // Check if $meta_bulk_async is running or queue has items (e.g. between batches).
     388            $is_generating = $this->meta_bulk_async->is_processing() || $this->meta_bulk_async->is_queued();
    389389
    390390            // Check if we're in selected images mode
     
    407407                        'status_generating'   => esc_html__( '🏃‍♂️‍➡️ Generating...', 'alt-text-imagerr-ai' ),
    408408                        'status_stopping_generation' => esc_html__( '🚫 Stopping generation...', 'alt-text-imagerr-ai' ),
     409                        'status_reconnecting' => esc_html__( 'Reconnecting…', 'alt-text-imagerr-ai' ),
    409410                        'image_updated' => esc_html__( '✅ Image updated', 'alt-text-imagerr-ai' ),
    410411                        'images_processed' => esc_html__( 'images processed', 'alt-text-imagerr-ai' ),
     
    703704        $credits = $this->meta->get_credits();
    704705        $status['credits'] = $credits;
    705         return new \WP_REST_Response( $status, 200 );
     706
     707        // If queue has work but process is not running (e.g. dispatch failed), try to wake it up (throttled).
     708        if ( $this->meta_bulk_async->is_queued() && ! $this->meta_bulk_async->is_processing() ) {
     709            $wakeup_transient = 'imagerr_meta_bulk_wakeup_attempted';
     710            if ( ! get_site_transient( $wakeup_transient ) ) {
     711                set_site_transient( $wakeup_transient, true, 60 );
     712                $this->meta_bulk_async->log_message( 'Dispatched from bulk-status (status check wake-up)' );
     713                $this->meta_bulk_async->dispatch();
     714            }
     715        }
     716
     717        $response = new \WP_REST_Response( $status, 200 );
     718        $response->header( 'Cache-Control', 'no-store, no-cache, must-revalidate' );
     719        $response->header( 'Pragma', 'no-cache' );
     720        return $response;
    706721    }
    707722
  • alt-text-imagerr-ai/trunk/readme.txt

    r3438971 r3460335  
    55Requires PHP: 5.2
    66Requires at least: 4.6
    7 Stable tag: 1.6
     7Stable tag: 1.6.1
    88Tested up to: 6.9
    99License: GPLv2 or later
     
    7171
    7272== Changelog ==
     73= 1.6.1 =
     74* Added extra debug logging to the background process for easier troubleshooting
     75* Refined bulk generation and background processing for better reliability
     76
    7377= 1.6 =
    7478* Added support for palette type images
  • alt-text-imagerr-ai/trunk/src/Async/BackgroundProcess.php

    r3438971 r3460335  
    105105
    106106    /**
     107     * Log a message to the debug file (public wrapper for callers outside the process).
     108     *
     109     * @param string $message The message to log.
     110     * @return void
     111     */
     112    public function log_message( $message ) {
     113        $this->log( $message );
     114    }
     115
     116    /**
    107117     * Log a message to the debug file.
    108118     *
     
    113123        // Only log if debug is enabled or if debug logs option is enabled
    114124        $debug_enabled = ( defined( 'IMAGERR_DEBUG' ) && IMAGERR_DEBUG ) || get_option( 'imagerr_enable_debug_logs', false );
    115        
     125
    116126        if ( $debug_enabled ) {
    117127            // Rotate log if it exceeds size limit
    118128            \Imagerr::rotate_log_if_needed();
    119            
     129
    120130            $log_file = \Imagerr::get_debug_log_path();
    121131            $timestamp = date( 'Y-m-d H:i:s' );
     
    140150        $this->schedule_event();
    141151
     152        // Get URL and log it for debugging
     153        $url = add_query_arg( $this->get_query_args(), $this->get_query_url() );
     154        $this->log( sprintf( 'Dispatching to URL: %s', $url ) );
     155
     156        // Check if URL is accessible (common issue with localhost/dev sites)
     157        $parsed_url = parse_url( $url );
     158        if ( isset( $parsed_url['host'] ) && in_array( $parsed_url['host'], array( 'localhost', '127.0.0.1', '::1' ), true ) ) {
     159            $this->log( 'WARNING: Using localhost URL - async requests may fail. Cron healthcheck will be used as fallback.' );
     160        }
     161
    142162        // Perform remote post.
    143163        $result = parent::dispatch();
    144        
     164
    145165        if ( is_wp_error( $result ) ) {
    146             $this->log( sprintf( 'Dispatch failed: %s', $result->get_error_message() ) );
     166            $this->log( sprintf( 'Dispatch failed with WP_Error: %s (code: %s)', $result->get_error_message(), $result->get_error_code() ) );
     167            $this->schedule_fallback_healthcheck();
    147168        } elseif ( false === $result ) {
    148169            $this->log( 'Dispatch returned false' );
     170            $this->schedule_fallback_healthcheck();
    149171        } else {
    150             $this->log( 'Dispatch successful' );
    151         }
    152        
     172            // Check response code even for non-blocking requests
     173            $response_code = wp_remote_retrieve_response_code( $result );
     174            if ( $response_code ) {
     175                $this->log( sprintf( 'Dispatch successful - Response code: %d', $response_code ) );
     176            } else {
     177                $this->log( 'Dispatch successful (non-blocking request - response code not available)' );
     178            }
     179            $response_body = wp_remote_retrieve_body( $result );
     180            $this->log( sprintf( 'Dispatch response body: %s', $response_body !== '' ? $response_body : '(empty)' ) );
     181            $this->log( 'Note: If processing does not start, cron healthcheck will handle it.' );
     182        }
     183
    153184        return $result;
    154185    }
     
    447478
    448479        $lock_acquired = set_site_transient( $this->identifier . '_process_lock', microtime(), $lock_duration );
    449        
     480
    450481        if ( ! $lock_acquired ) {
    451482            $this->log( sprintf( 'WARNING: Failed to acquire process lock (duration: %d seconds)', $lock_duration ) );
     
    464495    protected function unlock_process() {
    465496        $deleted = delete_site_transient( $this->identifier . '_process_lock' );
    466        
     497
    467498        if ( ! $deleted ) {
    468499            $this->log( 'WARNING: Failed to release process lock' );
     
    550581                        $batch->key  = $item->{$column};
    551582                        $batch->data = static::maybe_unserialize( $item->{$value_column}, $allowed_classes );
    552                        
     583
    553584                        if ( false === $batch->data && is_serialized( $item->{$value_column} ) ) {
    554585                            $this->log( sprintf( 'WARNING: Failed to unserialize batch data for key: %s', $batch->key ) );
    555586                            return null;
    556587                        }
    557                        
     588
    558589                        return $batch;
    559590                    } catch ( \Exception $e ) {
     
    564595                $items
    565596            );
    566            
     597
    567598            // Filter out null values
    568599            $batches = array_filter( $batches );
     
    609640
    610641            $batch = $this->get_batch();
    611            
     642
    612643            if ( empty( $batch ) || empty( $batch->data ) ) {
    613644                $this->log( 'No batch found or empty batch, stopping' );
    614645                break;
    615646            }
    616            
     647
    617648            $this->log( sprintf( 'Processing batch %s with %d items', $batch->key, count( $batch->data ) ) );
    618649
     
    699730
    700731        if ( $current_memory >= $memory_limit ) {
    701             $this->log( sprintf( 
    702                 'Memory limit exceeded: %s / %s (90%% of limit)', 
     732            $this->log( sprintf(
     733                'Memory limit exceeded: %s / %s (90%% of limit)',
    703734                size_format( $current_memory ),
    704735                size_format( $this->get_memory_limit() )
     
    746777
    747778        if ( time() >= $finish ) {
    748             $this->log( sprintf( 
    749                 'Time limit exceeded: %d seconds elapsed (limit: %d seconds)', 
     779            $this->log( sprintf(
     780                'Time limit exceeded: %d seconds elapsed (limit: %d seconds)',
    750781                $elapsed,
    751782                $time_limit
     
    828859
    829860    /**
     861     * Schedule a one-off healthcheck soon when immediate dispatch failed.
     862     * Ensures the queue resumes within ~30s instead of waiting for the next recurring cron.
     863     */
     864    protected function schedule_fallback_healthcheck() {
     865        $delay_seconds = (int) apply_filters( $this->identifier . '_fallback_healthcheck_delay', 30 );
     866        $delay_seconds = max( 10, min( 120, $delay_seconds ) );
     867        $when         = time() + $delay_seconds;
     868        $scheduled    = wp_schedule_single_event( $when, $this->cron_hook_identifier );
     869        if ( false === $scheduled || is_wp_error( $scheduled ) ) {
     870            $this->log( sprintf(
     871                'Fallback healthcheck schedule failed (in %d s). Recurring cron will retry.',
     872                $delay_seconds
     873            ) );
     874        } else {
     875            $this->log( sprintf(
     876                'Fallback healthcheck scheduled in %d seconds (hook: %s)',
     877                $delay_seconds,
     878                $this->cron_hook_identifier
     879            ) );
     880        }
     881    }
     882
     883    /**
    830884     * Handle cron healthcheck event.
    831885     *
     
    835889    public function handle_cron_healthcheck() {
    836890        $this->log( 'Cron healthcheck triggered' );
    837        
     891
    838892        if ( $this->is_processing() ) {
    839893            $lock_time = get_site_transient( $this->identifier . '_process_lock' );
    840894            $lock_age = $lock_time ? time() - (int) $lock_time : 0;
    841            
    842             $this->log( sprintf( 
    843                 'Process already running (lock age: %d seconds), skipping', 
     895
     896            $this->log( sprintf(
     897                'Process already running (lock age: %d seconds), skipping',
    844898                $lock_age
    845899            ) );
     
    861915     */
    862916    protected function schedule_event() {
    863         if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
    864             wp_schedule_event( time() + ( $this->get_cron_interval() * MINUTE_IN_SECONDS ), $this->cron_interval_identifier, $this->cron_hook_identifier );
     917        $next_scheduled = wp_next_scheduled( $this->cron_hook_identifier );
     918        $interval      = $this->get_cron_interval();
     919
     920        if ( $next_scheduled ) {
     921            $this->log( sprintf(
     922                'Cron event already scheduled (hook: %s, interval: %d min). Next run: %s',
     923                $this->cron_hook_identifier,
     924                $interval,
     925                gmdate( 'Y-m-d H:i:s', $next_scheduled )
     926            ) );
     927            return;
     928        }
     929
     930        $first_run = time() + ( $interval * MINUTE_IN_SECONDS );
     931        $scheduled = wp_schedule_event( $first_run, $this->cron_interval_identifier, $this->cron_hook_identifier );
     932
     933        if ( false === $scheduled || is_wp_error( $scheduled ) ) {
     934            $this->log( sprintf(
     935                'Cron schedule FAILED (hook: %s, interval: %d min). wp_schedule_event returned: %s',
     936                $this->cron_hook_identifier,
     937                $interval,
     938                is_wp_error( $scheduled ) ? $scheduled->get_error_message() : 'false'
     939            ) );
     940        } else {
     941            $this->log( sprintf(
     942                'Cron event scheduled successfully (hook: %s, interval: %d min). First run: %s',
     943                $this->cron_hook_identifier,
     944                $interval,
     945                gmdate( 'Y-m-d H:i:s', $first_run )
     946            ) );
    865947        }
    866948    }
     
    874956        if ( $timestamp ) {
    875957            wp_unschedule_event( $timestamp, $this->cron_hook_identifier );
     958            $this->log( sprintf(
     959                'Cron event cleared (hook: %s, was scheduled for: %s)',
     960                $this->cron_hook_identifier,
     961                gmdate( 'Y-m-d H:i:s', $timestamp )
     962            ) );
     963        } else {
     964            $this->log( sprintf(
     965                'Cron event clear: nothing scheduled (hook: %s)',
     966                $this->cron_hook_identifier
     967            ) );
    876968        }
    877969    }
  • alt-text-imagerr-ai/trunk/src/MetaBulkAsync.php

    r3434623 r3460335  
    3030     */
    3131    protected $action = 'generate_meta_bulk';
     32
     33    /**
     34     * Cron healthcheck interval in minutes.
     35     * Shorter than default (5) so fallback cron resumes queue sooner if dispatch fails.
     36     *
     37     * @var int
     38     */
     39    protected $cron_interval = 1;
    3240
    3341    /**
Note: See TracChangeset for help on using the changeset viewer.