__( 'Insufficient permissions.', 'ai-virtual-try-on-for-woocommerce' ) ) ); } // Get parameters $product_id = isset( $_POST['product_id'] ) ? absint( $_POST['product_id'] ) : 0; $garment_image = isset( $_POST['garment_image'] ) ? esc_url_raw( wp_unslash( $_POST['garment_image'] ) ) : ''; $model_image = isset( $_POST['model_image'] ) ? esc_url_raw( wp_unslash( $_POST['model_image'] ) ) : ''; $category = isset( $_POST['category'] ) ? sanitize_text_field( wp_unslash( $_POST['category'] ) ) : 'upper_body'; $garment_description = isset( $_POST['garment_description'] ) ? sanitize_text_field( wp_unslash( $_POST['garment_description'] ) ) : ''; $custom_filename = isset( $_POST['custom_filename'] ) ? sanitize_file_name( wp_unslash( $_POST['custom_filename'] ) ) : ''; $model_gender = isset( $_POST['model_gender'] ) ? sanitize_text_field( wp_unslash( $_POST['model_gender'] ) ) : ''; $auto_crop = isset( $_POST['auto_crop'] ) ? (bool) absint( $_POST['auto_crop'] ) : false; // Validate if ( empty( $product_id ) ) { wp_send_json_error( array( 'message' => __( 'Product ID is required.', 'ai-virtual-try-on-for-woocommerce' ) ) ); } if ( empty( $garment_image ) ) { wp_send_json_error( array( 'message' => __( 'Garment image is required.', 'ai-virtual-try-on-for-woocommerce' ) ) ); } if ( empty( $model_image ) ) { wp_send_json_error( array( 'message' => __( 'Model image is required.', 'ai-virtual-try-on-for-woocommerce' ) ) ); } // Generate image using Replicate API $api = new RWPVTO_Replicate_API(); $result = $api->generate_tryon( array( 'garm_img' => $garment_image, 'human_img' => $model_image, 'garment_des' => $garment_description, 'category' => $category, 'crop' => $auto_crop, ) ); // Check for errors if ( is_wp_error( $result ) ) { wp_send_json_error( array( 'message' => $result->get_error_message() ) ); } // Automatically download image to WordPress uploads $local_image_url = null; $attachment_id = $this->download_and_attach_image( $result['output_url'], $product_id, null, array( 'garm_img' => $garment_image, 'human_img' => $model_image, 'category' => $category, 'garment_des' => $garment_description, 'custom_filename' => $custom_filename, 'model_gender' => $model_gender, ) ); if ( ! is_wp_error( $attachment_id ) ) { $local_image_url = wp_get_attachment_url( $attachment_id ); } // Save to database $db = RWPVTO_Database::instance(); $image_id = $db->insert_image( array( 'product_id' => $product_id, 'garment_image_url' => $garment_image, 'model_image_url' => $model_image, 'generated_image_url' => $result['output_url'], 'local_image_url' => $local_image_url, 'replicate_prediction_id' => $result['prediction_id'], 'model_gender' => $model_gender, 'model_ethnicity' => null, 'category' => $category, 'garment_description' => $garment_description, 'parameters' => array( 'garm_img' => $garment_image, 'human_img' => $model_image, 'category' => $category, 'garment_des' => $garment_description, 'custom_filename' => $custom_filename, ), 'status' => 'completed', ) ); if ( ! $image_id ) { wp_send_json_error( array( 'message' => __( 'Failed to save generated image.', 'ai-virtual-try-on-for-woocommerce' ) ) ); } // Use local URL if available, otherwise use replicate URL $display_url = $local_image_url ? $local_image_url : $result['output_url']; wp_send_json_success( array( 'message' => __( 'Image generated successfully!', 'ai-virtual-try-on-for-woocommerce' ), 'image_id' => $image_id, 'image_url' => $display_url, 'local_image_url' => $local_image_url, 'prediction_id' => $result['prediction_id'], ) ); } /** * Delete generated image */ public function delete_image() { // Verify nonce check_ajax_referer( 'rwpvto_nonce', 'nonce' ); // Check user capabilities if ( ! current_user_can( 'edit_products' ) ) { wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'ai-virtual-try-on-for-woocommerce' ) ) ); } $image_id = isset( $_POST['image_id'] ) ? absint( $_POST['image_id'] ) : 0; if ( empty( $image_id ) ) { wp_send_json_error( array( 'message' => __( 'Image ID is required.', 'ai-virtual-try-on-for-woocommerce' ) ) ); } $db = RWPVTO_Database::instance(); $result = $db->delete_image( $image_id ); if ( $result ) { wp_send_json_success( array( 'message' => __( 'Image deleted successfully.', 'ai-virtual-try-on-for-woocommerce' ) ) ); } else { wp_send_json_error( array( 'message' => __( 'Failed to delete image.', 'ai-virtual-try-on-for-woocommerce' ) ) ); } } /** * Get generated images for product */ public function get_product_images() { // Verify nonce check_ajax_referer( 'rwpvto_nonce', 'nonce' ); // Check user capabilities if ( ! current_user_can( 'edit_products' ) ) { wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'ai-virtual-try-on-for-woocommerce' ) ) ); } $product_id = isset( $_POST['product_id'] ) ? absint( $_POST['product_id'] ) : 0; if ( empty( $product_id ) ) { wp_send_json_error( array( 'message' => __( 'Product ID is required.', 'ai-virtual-try-on-for-woocommerce' ) ) ); } $db = RWPVTO_Database::instance(); $images = $db->get_images_by_product( $product_id ); wp_send_json_success( array( 'images' => $images ) ); } /** * Add generated image to product gallery */ public function add_to_gallery() { // Verify nonce check_ajax_referer( 'rwpvto_nonce', 'nonce' ); // Check user capabilities if ( ! current_user_can( 'edit_products' ) ) { wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'ai-virtual-try-on-for-woocommerce' ) ) ); } $image_id = isset( $_POST['image_id'] ) ? absint( $_POST['image_id'] ) : 0; $product_id = isset( $_POST['product_id'] ) ? absint( $_POST['product_id'] ) : 0; if ( empty( $image_id ) || empty( $product_id ) ) { wp_send_json_error( array( 'message' => __( 'Image ID and Product ID are required.', 'ai-virtual-try-on-for-woocommerce' ) ) ); } // Get image from database $db = RWPVTO_Database::instance(); $image = $db->get_image( $image_id ); if ( ! $image ) { wp_send_json_error( array( 'message' => __( 'Image not found.', 'ai-virtual-try-on-for-woocommerce' ) ) ); } // Download image and add to media library (if not already downloaded) $attachment_id = null; $local_image_url = null; if ( ! empty( $image->local_image_url ) ) { // Image already downloaded, get attachment ID from URL $attachment_id = attachment_url_to_postid( $image->local_image_url ); $local_image_url = $image->local_image_url; } else { // Download image and add to media library $attachment_id = $this->download_and_attach_image( $image->generated_image_url, $product_id, $image ); if ( is_wp_error( $attachment_id ) ) { wp_send_json_error( array( 'message' => $attachment_id->get_error_message() ) ); } // Get local URL and update database $local_image_url = wp_get_attachment_url( $attachment_id ); if ( $local_image_url ) { $db->update_image( $image->id, array( 'local_image_url' => $local_image_url ) ); } } // Add to product gallery $product = wc_get_product( $product_id ); if ( ! $product ) { wp_send_json_error( array( 'message' => __( 'Product not found.', 'ai-virtual-try-on-for-woocommerce' ) ) ); } $gallery_ids = $product->get_gallery_image_ids(); // Check if image already exists in gallery if ( in_array( $attachment_id, $gallery_ids, true ) ) { wp_send_json_error( array( 'message' => __( 'This image is already in the product gallery.', 'ai-virtual-try-on-for-woocommerce' ) ) ); } // Add to gallery $gallery_ids[] = $attachment_id; $product->set_gallery_image_ids( $gallery_ids ); $product->save(); wp_send_json_success( array( 'message' => __( 'Image added to product gallery.', 'ai-virtual-try-on-for-woocommerce' ), 'attachment_id' => $attachment_id, ) ); } /** * Test API connection */ public function test_api() { // Verify nonce check_ajax_referer( 'rwpvto_nonce', 'nonce' ); // Check user capabilities if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'ai-virtual-try-on-for-woocommerce' ) ) ); } $api = new RWPVTO_Replicate_API(); $result = $api->test_connection(); if ( is_wp_error( $result ) ) { wp_send_json_error( array( 'message' => $result->get_error_message() ) ); } wp_send_json_success( array( 'message' => __( 'API connection successful!', 'ai-virtual-try-on-for-woocommerce' ) ) ); } /** * Download image and attach to product * * @param string $image_url Image URL * @param int $product_id Product ID * @param object|null $image_data Image data from database (optional) * @param array|null $params Additional parameters for filename generation (optional) * @return int|WP_Error Attachment ID or error */ private function download_and_attach_image( $image_url, $product_id, $image_data = null, $params = null ) { require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/media.php'; require_once ABSPATH . 'wp-admin/includes/image.php'; // Set timeout for external image download add_filter( 'http_request_timeout', array( $this, 'increase_download_timeout' ) ); // Download file with increased timeout $tmp = download_url( $image_url, 60 ); // 60 seconds timeout // Remove filter remove_filter( 'http_request_timeout', array( $this, 'increase_download_timeout' ) ); if ( is_wp_error( $tmp ) ) { return new WP_Error( 'download_failed', sprintf( /* translators: %s: Error message */ __( 'Failed to download image: %s', 'ai-virtual-try-on-for-woocommerce' ), $tmp->get_error_message() ) ); } // Detect file extension from URL or default to png $file_ext = 'png'; $parsed_url = wp_parse_url( $image_url ); if ( isset( $parsed_url['path'] ) ) { $path_info = pathinfo( $parsed_url['path'] ); if ( isset( $path_info['extension'] ) ) { $file_ext = strtolower( $path_info['extension'] ); } } // Validate and sanitize file extension (support common image formats) $allowed_extensions = array( 'jpg', 'jpeg', 'png', 'gif', 'webp' ); if ( ! in_array( $file_ext, $allowed_extensions, true ) ) { $file_ext = 'png'; // Default to png if unknown extension } // Check for custom filename first $custom_filename = ''; $model_gender = null; // Try to get parameters from $params first (for new images), then from $image_data if ( $params && isset( $params['custom_filename'] ) ) { $custom_filename = $params['custom_filename']; } elseif ( $image_data && ! empty( $image_data->parameters ) ) { // Parameters are stored as JSON, so decode if needed $parameters = $image_data->parameters; if ( is_string( $parameters ) ) { $parameters = json_decode( $parameters, true ); } if ( is_array( $parameters ) && isset( $parameters['custom_filename'] ) ) { $custom_filename = $parameters['custom_filename']; } } // Get model_gender from params or image_data if ( $params && isset( $params['model_gender'] ) ) { $model_gender = $params['model_gender']; } elseif ( $image_data && ! empty( $image_data->model_gender ) ) { $model_gender = $image_data->model_gender; } // Get category from params or image_data $category = null; if ( $params && isset( $params['category'] ) ) { $category = $params['category']; } elseif ( $image_data && ! empty( $image_data->category ) ) { $category = $image_data->category; } // Use custom filename if provided, otherwise build SEO-friendly filename if ( ! empty( $custom_filename ) ) { // Sanitize custom filename $filename = sanitize_file_name( $custom_filename ); // Remove any file extension if user included it $filename = preg_replace( '/\.[^.]+$/', '', $filename ); // Add timestamp for uniqueness $filename = $filename . '-' . time(); } else { // Build SEO-friendly filename $filename_parts = array(); // Get product name/slug $product = wc_get_product( $product_id ); if ( $product ) { $product_slug = sanitize_file_name( $product->get_slug() ); if ( empty( $product_slug ) ) { $product_slug = sanitize_file_name( $product->get_name() ); } if ( ! empty( $product_slug ) ) { $filename_parts[] = $product_slug; } } // Add model info if available if ( ! empty( $model_gender ) ) { $gender = strtolower( sanitize_file_name( $model_gender ) ); $filename_parts[] = $gender; } // Add category if available if ( ! empty( $category ) ) { $category_sanitized = sanitize_file_name( $category ); // Convert category to readable format $category_map = array( 'upper_body' => 'upper-body', 'lower_body' => 'lower-body', 'dresses' => 'dress', ); $category_sanitized = isset( $category_map[ $category_sanitized ] ) ? $category_map[ $category_sanitized ] : $category_sanitized; $filename_parts[] = $category_sanitized; } // Add virtual-tryon prefix array_unshift( $filename_parts, 'virtual-tryon' ); // Add timestamp for uniqueness $filename_parts[] = time(); // Join parts and create filename $filename = implode( '-', $filename_parts ); } // Limit filename length (max 200 chars for filesystem compatibility) if ( strlen( $filename ) > 200 ) { $filename = substr( $filename, 0, 200 ); } // Get file info $file_array = array( 'name' => $filename . '.' . $file_ext, 'tmp_name' => $tmp, ); // Upload to media library $attachment_id = media_handle_sideload( $file_array, $product_id ); // Check for errors if ( is_wp_error( $attachment_id ) ) { wp_delete_file( $file_array['tmp_name'] ); return new WP_Error( 'upload_failed', sprintf( /* translators: %s: Error message */ __( 'Failed to upload to media library: %s', 'ai-virtual-try-on-for-woocommerce' ), $attachment_id->get_error_message() ) ); } return $attachment_id; } /** * Increase download timeout for external images * * @param int $timeout Current timeout * @return int New timeout */ public function increase_download_timeout( $timeout ) { return 60; // 60 seconds } }