30, ) ); if ( is_wp_error( $response ) ) { throw new \RuntimeException( sprintf( esc_html__( 'Runware image download failed: %s', 'prautoblogger' ), esc_html( $response->get_error_message() ) ) ); } $status = (int) wp_remote_retrieve_response_code( $response ); if ( 200 !== $status ) { throw new \RuntimeException( sprintf( esc_html__( 'Runware image download HTTP %d.', 'prautoblogger' ), $status ) ); } $bytes = (string) wp_remote_retrieve_body( $response ); if ( '' === $bytes ) { throw new \RuntimeException( esc_html__( 'Runware image download returned empty body.', 'prautoblogger' ) ); } return $bytes; } /** * Snap a target pixel dimension to Runware's FLUX constraints. * * FLUX.1 (schnell + dev) requires dimensions divisible by 64, in the * range [512, 2048]. We round to the nearest multiple of 64 and clamp. * * @param int $px Caller-requested pixel dimension. * @return int Snapped dimension. */ public function snap_dimension( int $px ): int { $snapped = (int) round( $px / 64 ) * 64; if ( $snapped < 512 ) { return 512; } if ( $snapped > 2048 ) { return 2048; } return $snapped; } /** * Normalize a caller seed to Runware's constraint (must be > 0). * * @param int|null $seed Caller-supplied seed or null for "random". * @return int A positive integer seed. */ public function normalize_seed( ?int $seed ): int { if ( null === $seed || $seed < 1 ) { $derived = (int) ( ( microtime( true ) * 1000 ) % 2147483647 ); return $derived > 0 ? $derived : 1; } return $seed; } /** * Emit a WARN line for a retryable image-gen failure. * * @param int $attempt 1-based attempt number. * @param string $class 'network' or HTTP status code string. * @param string $detail Short failure detail. */ public function log_retry( int $attempt, string $class, string $detail ): void { PRAutoBlogger_Logger::instance()->warning( sprintf( 'Runware image gen %s failure (attempt %d/%d): %s', $class, $attempt, PRAUTOBLOGGER_MAX_RETRIES, $detail ), 'runware-image' ); } /** * Sleep between retries with exponential backoff. * * @param int $attempt 1-based attempt that just failed. * @param int $retry_after Retry-After header value (0 if absent). */ public function backoff( int $attempt, int $retry_after = 0 ): void { $delay = $retry_after > 0 ? min( $retry_after, 60 ) : PRAUTOBLOGGER_RETRY_BASE_DELAY_SECONDS * (int) pow( 2, $attempt - 1 ); if ( $delay > 0 ) { sleep( $delay ); } } }