$types, 'post_status' => 'publish', 'fields' => 'ids', 'posts_per_page' => 20, 'orderby' => 'ID', 'order' => 'ASC', 'no_found_rows' => true, 'update_post_term_cache' => false, 'meta_query' => array( 'relation' => 'AND', array( 'relation' => 'OR', array( 'key' => 'accessibility_audio_tts_generated', 'compare' => 'NOT EXISTS' ), array( 'key' => 'accessibility_audio_tts_generated', 'value' => array( '', '0' ), 'compare' => 'IN' ), ), array( 'relation' => 'OR', array( 'key' => self::RETRY_STATE_META, 'compare' => 'NOT EXISTS' ), array( 'key' => self::RETRY_STATE_META, 'value' => array( 'permanent', 'exhausted' ), 'compare' => 'NOT IN' ), ), array( 'relation' => 'OR', array( 'key' => self::RETRY_AFTER_META, 'compare' => 'NOT EXISTS' ), array( 'key' => self::RETRY_AFTER_META, 'value' => time(), 'compare' => '<=', 'type' => 'NUMERIC' ), ), ), ) ); $ids = array_map( 'intval', $query->posts ); Atomic_Options::update( self::QUEUE_OPTION, static function ( $queue ) use ( $ids ) { return array_values( array_unique( array_merge( self::normalize_queue( $queue ), $ids ) ) ); } ); } private static function normalize_queue( $queue ) { return is_array( $queue ) ? array_values( array_filter( array_map( 'intval', $queue ), static function ( $id ) { return $id > 0; } ) ) : array(); } public function get_pending_queue() { return self::normalize_queue( get_option( self::QUEUE_OPTION, array() ) ); } public function get_queue_size() { return count( $this->get_pending_queue() ); } public function get_next_post() { $queue = $this->get_pending_queue(); return $queue[0] ?? null; } /** Explicit retry re-enables a terminal job; automatic discovery never does. */ public function add_to_queue( $post_id, $manual_retry = true ) { $post_id = absint( $post_id ); if ( ! $post_id ) { return; } if ( $manual_retry ) { $this->clear_retry_state( $post_id ); } Atomic_Options::update( self::QUEUE_OPTION, static function ( $queue ) use ( $post_id ) { $queue = self::normalize_queue( $queue ); $queue[] = $post_id; return array_values( array_unique( $queue ) ); } ); } public function remove_from_queue( $post_id ) { $post_id = absint( $post_id ); Atomic_Options::update( self::QUEUE_OPTION, static function ( $queue ) use ( $post_id ) { return array_values( array_diff( self::normalize_queue( $queue ), array( $post_id ) ) ); } ); } /** An exact-value CAS makes simultaneous workers claim distinct items. */ public function process_next_and_remove() { $claimed = null; Atomic_Options::update( self::QUEUE_OPTION, static function ( $queue ) use ( &$claimed ) { $queue = self::normalize_queue( $queue ); $claimed = array_shift( $queue ); return $queue; } ); return $claimed; } public function retry_is_due( $post_id ) { $state = get_post_meta( $post_id, self::RETRY_STATE_META, true ); return ! in_array( $state, array( 'permanent', 'exhausted' ), true ) && (int) get_post_meta( $post_id, self::RETRY_AFTER_META, true ) <= time(); } public function clear_retry_state( $post_id ) { foreach ( array( self::RETRY_META, self::RETRY_AFTER_META, self::RETRY_STATE_META ) as $key ) { delete_post_meta( $post_id, $key ); } } /** Unknown provider errors get finite backoff; only structurally invalid posts are permanent. */ public function record_failure( $post_id, array $result, $permanent = false ) { $old = get_post_meta( $post_id, self::RETRY_META, true ); $attempts = ( is_array( $old ) ? (int) ( $old['attempts'] ?? 0 ) : 0 ) + 1; $state = $permanent ? 'permanent' : ( $attempts >= self::MAX_ATTEMPTS ? 'exhausted' : 'deferred' ); $after = 'deferred' === $state ? time() + min( HOUR_IN_SECONDS, 60 * ( 2 ** ( $attempts - 1 ) ) ) : 0; update_post_meta( $post_id, self::RETRY_META, array( 'attempts' => $attempts, 'state' => $state, 'retry_after' => $after, 'error' => sanitize_text_field( $result['error'] ?? 'Audio generation failed.' ) ) ); update_post_meta( $post_id, self::RETRY_STATE_META, $state ); update_post_meta( $post_id, self::RETRY_AFTER_META, $after ); update_post_meta( $post_id, 'accessibility_audio_tts_generation_status', 'failed' ); } }