Last active
January 13, 2026 12:29
-
-
Save iamkingsleyf/3fd89285ee732ea7e70543f12f5fdc3b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * File: class-idb-mobileapi-api.php | |
| * Author: Krafty Sprouts Media, LLC | |
| * Created: 12/01/2026 | |
| * Last Modified: 12/01/2026 | |
| * Description: MobileAPI.dev API integration for IDB plugin. Wrapper for MobileAPI.dev Device Specs API. | |
| * | |
| * @package IDB | |
| * @subpackage IDB/includes/api | |
| * @author Krafty Sprouts Media, LLC | |
| * @since 7.9.2 | |
| */ | |
| // If this file is called directly, abort. | |
| if (!defined('ABSPATH')) { | |
| exit; | |
| } | |
| /** | |
| * MobileAPI.dev API wrapper class. | |
| * | |
| * Handles all communication with MobileAPI.dev API including rate limiting, | |
| * error handling, retries, and response caching. | |
| * | |
| * @since 7.9.2 | |
| */ | |
| class IDB_MobileAPI_API | |
| { | |
| /** | |
| * MobileAPI.dev API base URL. | |
| * | |
| * @since 7.9.2 | |
| * @var string | |
| */ | |
| private $api_base_url = 'https://api.mobileapi.dev/'; | |
| /** | |
| * MobileAPI.dev API key. | |
| * | |
| * @since 7.9.2 | |
| * @var string | |
| */ | |
| private $api_key; | |
| /** | |
| * Request counter for rate limiting. | |
| * | |
| * @since 7.9.2 | |
| * @var array | |
| */ | |
| private $request_log = array(); | |
| /** | |
| * Maximum requests per time window (MobileAPI.dev limits vary by plan). | |
| * | |
| * @since 7.9.2 | |
| * @var int | |
| */ | |
| private $max_requests = 100; | |
| /** | |
| * Time window in seconds for rate limiting. | |
| * | |
| * @since 7.9.2 | |
| * @var int | |
| */ | |
| private $time_window = 60; | |
| /** | |
| * Constructor. | |
| * | |
| * @since 7.9.2 | |
| */ | |
| public function __construct() | |
| { | |
| $this->api_key = get_option('idb_mobileapi_api_key', ''); | |
| } | |
| /** | |
| * Check if MobileAPI.dev API key is configured. | |
| * | |
| * @since 7.9.2 | |
| * @return bool True if API key is set, false otherwise. | |
| */ | |
| public function is_configured() | |
| { | |
| return !empty($this->api_key); | |
| } | |
| /** | |
| * Validate MobileAPI.dev API key. | |
| * | |
| * @since 7.9.2 | |
| * @param string $api_key Optional API key to validate. Uses stored key if not provided. | |
| * @return bool|WP_Error True if valid, WP_Error on failure. | |
| */ | |
| public function validate_api_key($api_key = '') | |
| { | |
| if (empty($api_key)) { | |
| $api_key = $this->api_key; | |
| } | |
| if (empty($api_key)) { | |
| return new WP_Error('no_api_key', __('MobileAPI.dev API key not configured.', 'idb')); | |
| } | |
| // Test API key by making a simple request (get manufacturers list). | |
| $result = $this->make_request('manufacturers/', array('key' => $api_key)); | |
| if (is_wp_error($result)) { | |
| // Check if it's an authentication error. | |
| if ($result->get_error_code() === 'unauthorized') { | |
| return new WP_Error('invalid_api_key', __('Invalid MobileAPI.dev API key.', 'idb')); | |
| } | |
| return $result; | |
| } | |
| return true; | |
| } | |
| /** | |
| * Make API request to MobileAPI.dev. | |
| * | |
| * @since 7.9.2 | |
| * @param string $endpoint API endpoint (relative to base URL). | |
| * @param array $params Query parameters. | |
| * @param int $retry_count Internal retry counter. | |
| * @return array|WP_Error Response data or WP_Error on failure. | |
| */ | |
| private function make_request($endpoint, $params = array(), $retry_count = 0) | |
| { | |
| // Log request for debugging. | |
| error_log('IDB MobileAPI: Making request to ' . $endpoint . ' (retry: ' . $retry_count . ')'); | |
| if (empty($this->api_key)) { | |
| return new WP_Error('no_api_key', __('MobileAPI.dev API key not configured.', 'idb')); | |
| } | |
| // Add API key to params if not already present. | |
| if (!isset($params['key'])) { | |
| $params['key'] = $this->api_key; | |
| } | |
| // Build URL. | |
| $url = $this->api_base_url . $endpoint; | |
| $url = add_query_arg($params, $url); | |
| // Check cache first. | |
| $cache_enabled = get_option('idb_cache_api_responses', true); | |
| if ($cache_enabled) { | |
| $cache_key = 'idb_mobileapi_' . md5($url); | |
| $cached_response = get_transient($cache_key); | |
| if ($cached_response !== false) { | |
| return $cached_response; | |
| } | |
| } | |
| // Rate limiting check. | |
| $this->enforce_rate_limit(); | |
| // Make request with shorter timeout for search (search can be slow). | |
| $timeout = 30; // Increased from 15 to 30 seconds for slow API responses | |
| if (strpos($endpoint, 'search') !== false) { | |
| $timeout = 20; // Search requests get 20 second timeout | |
| } | |
| $response = wp_remote_get($url, array( | |
| 'timeout' => $timeout, | |
| 'sslverify' => true, | |
| 'headers' => array( | |
| 'Accept' => 'application/json', | |
| ), | |
| )); | |
| // Log request time for rate limiting. | |
| $this->log_request(); | |
| // Handle errors. | |
| if (is_wp_error($response)) { | |
| error_log('IDB MobileAPI.dev API Request Error: ' . $response->get_error_message()); | |
| // Retry logic for transient errors. | |
| if ($retry_count < 3) { | |
| sleep(1); // Wait 1 second before retry. | |
| return $this->make_request($endpoint, $params, $retry_count + 1); | |
| } | |
| return $response; | |
| } | |
| // Check HTTP status code. | |
| $code = wp_remote_retrieve_response_code($response); | |
| $body = wp_remote_retrieve_body($response); | |
| // Log response for debugging | |
| error_log('IDB MobileAPI: Response code: ' . $code . ', Body length: ' . strlen($body) . ' bytes'); | |
| if (strlen($body) < 500) { | |
| error_log('IDB MobileAPI: Response body: ' . substr($body, 0, 500)); | |
| } | |
| if ($code === 200) { | |
| $data = json_decode($body, true); | |
| if (json_last_error() !== JSON_ERROR_NONE) { | |
| error_log('IDB MobileAPI.dev API JSON Decode Error: ' . json_last_error_msg() . ' - Body: ' . substr($body, 0, 200)); | |
| return new WP_Error('json_decode_error', __('Failed to decode MobileAPI.dev API response.', 'idb')); | |
| } | |
| // Log successful response structure | |
| error_log('IDB MobileAPI: Successfully decoded response. Keys: ' . implode(', ', array_keys($data ?? array()))); | |
| // Cache successful response. | |
| if ($cache_enabled) { | |
| $cache_duration = get_option('idb_cache_duration', 12) * HOUR_IN_SECONDS; | |
| set_transient($cache_key, $data, $cache_duration); | |
| } | |
| return $data; | |
| } elseif ($code === 204) { | |
| // 204 = No device found (valid response, not an error) | |
| error_log('IDB MobileAPI: No devices found (204 response)'); | |
| return array('devices' => array(), 'total' => 0, 'page' => 1, 'total_pages' => 0); | |
| } elseif ($code === 429) { | |
| // Rate limit exceeded - DO NOT RETRY (retries make it worse). | |
| error_log('IDB MobileAPI.dev API Rate Limit Exceeded - Stopping request to prevent further rate limit hits'); | |
| return new WP_Error('rate_limit_exceeded', __('MobileAPI.dev API rate limit exceeded. Please wait before trying again.', 'idb')); | |
| } elseif ($code === 404) { | |
| return new WP_Error('not_found', __('Device not found on MobileAPI.dev.', 'idb')); | |
| } elseif ($code === 401) { | |
| return new WP_Error('unauthorized', __('Invalid MobileAPI.dev API key.', 'idb')); | |
| } else { | |
| error_log('IDB MobileAPI.dev API HTTP Error: ' . $code . ' - ' . $body); | |
| return new WP_Error('api_error', sprintf(__('MobileAPI.dev API returned error code: %d', 'idb'), $code)); | |
| } | |
| } | |
| /** | |
| * Enforce rate limiting. | |
| * | |
| * @since 7.9.2 | |
| * @return void | |
| */ | |
| private function enforce_rate_limit() | |
| { | |
| // Clean up old requests from log. | |
| $current_time = time(); | |
| $this->request_log = array_filter($this->request_log, function ($timestamp) use ($current_time) { | |
| return ($current_time - $timestamp) < $this->time_window; | |
| }); | |
| // Check if we've exceeded the limit. | |
| if (count($this->request_log) >= $this->max_requests) { | |
| // Wait until oldest request expires. | |
| $oldest_request = min($this->request_log); | |
| $wait_time = $this->time_window - ($current_time - $oldest_request) + 1; | |
| if ($wait_time > 0) { | |
| sleep($wait_time); | |
| } | |
| } | |
| } | |
| /** | |
| * Log request timestamp for rate limiting. | |
| * | |
| * @since 7.9.2 | |
| * @return void | |
| */ | |
| private function log_request() | |
| { | |
| $this->request_log[] = time(); | |
| // Keep log size reasonable (max 200 entries). | |
| if (count($this->request_log) > 200) { | |
| $this->request_log = array_slice($this->request_log, -200); | |
| } | |
| } | |
| /** | |
| * Search devices by name. | |
| * | |
| * @since 7.9.2 | |
| * @param string $name Device name to search for. | |
| * @param string $manufacturer Optional manufacturer filter. | |
| * @param int $page Page number (default: 1). | |
| * @param int $limit Number of results per page (default: 30, max: 30 for search endpoint). | |
| * @return array|WP_Error Device search results or WP_Error on failure. | |
| */ | |
| public function search_devices($name, $manufacturer = '', $page = 1, $limit = 30) | |
| { | |
| $params = array( | |
| 'name' => $name, | |
| 'page' => $page, | |
| ); | |
| // Add limit parameter if specified (max 30 for search endpoint) | |
| if ($limit > 0 && $limit <= 30) { | |
| $params['limit'] = $limit; | |
| } | |
| if (!empty($manufacturer)) { | |
| $params['manufacturer'] = $manufacturer; | |
| } | |
| return $this->make_request('devices/search/', $params); | |
| } | |
| /** | |
| * Get device by ID. | |
| * | |
| * @since 7.9.2 | |
| * @param int $device_id Device ID. | |
| * @return array|WP_Error Device data or WP_Error on failure. | |
| */ | |
| public function get_device($device_id) | |
| { | |
| return $this->make_request('devices/' . absint($device_id) . '/'); | |
| } | |
| /** | |
| * Get device autocomplete suggestions. | |
| * | |
| * @since 7.9.2 | |
| * @param string $query Search query. | |
| * @param int $limit Limit results (default: 10). | |
| * @return array|WP_Error Autocomplete suggestions or WP_Error on failure. | |
| */ | |
| public function autocomplete($query, $limit = 10) | |
| { | |
| $params = array( | |
| 'q' => $query, | |
| 'limit' => absint($limit), | |
| ); | |
| return $this->make_request('devices/autocomplete/', $params); | |
| } | |
| /** | |
| * Get devices by manufacturer. | |
| * | |
| * @since 7.9.2 | |
| * @param string $manufacturer Manufacturer name or ID. | |
| * @param int $page Page number (default: 1). | |
| * @return array|WP_Error Devices or WP_Error on failure. | |
| */ | |
| public function get_devices_by_manufacturer($manufacturer, $page = 1) | |
| { | |
| $params = array( | |
| 'manufacturer' => $manufacturer, | |
| 'page' => $page, | |
| ); | |
| return $this->make_request('devices/by-manufacturer/', $params); | |
| } | |
| /** | |
| * Get devices by type. | |
| * | |
| * @since 7.9.2 | |
| * @param string $type Device type (phone, tablet, laptop, wearable, other). | |
| * @param int $page Page number (default: 1). | |
| * @return array|WP_Error Devices or WP_Error on failure. | |
| */ | |
| public function get_devices_by_type($type, $page = 1) | |
| { | |
| $valid_types = array('phone', 'tablet', 'laptop', 'wearable', 'other'); | |
| if (!in_array($type, $valid_types, true)) { | |
| return new WP_Error('invalid_type', __('Invalid device type. Must be: phone, tablet, laptop, wearable, or other.', 'idb')); | |
| } | |
| $params = array( | |
| 'type' => $type, | |
| 'page' => $page, | |
| ); | |
| return $this->make_request('devices/by-type/', $params); | |
| } | |
| /** | |
| * Get device specifications by category. | |
| * | |
| * @since 7.9.2 | |
| * @param int $device_id Device ID. | |
| * @param string $category Specification category (network, battery, display, platform, memory, main-camera, selfie-camera, body, sound, comms, features, misc). | |
| * @return array|WP_Error Specification data or WP_Error on failure. | |
| */ | |
| public function get_device_specs($device_id, $category) | |
| { | |
| $valid_categories = array( | |
| 'network', | |
| 'battery', | |
| 'display', | |
| 'platform', | |
| 'memory', | |
| 'main-camera', | |
| 'selfie-camera', | |
| 'body', | |
| 'sound', | |
| 'comms', | |
| 'features', | |
| 'misc', | |
| ); | |
| if (!in_array($category, $valid_categories, true)) { | |
| return new WP_Error('invalid_category', __('Invalid specification category.', 'idb')); | |
| } | |
| return $this->make_request('devices/' . absint($device_id) . '/' . $category . '/'); | |
| } | |
| /** | |
| * Get all device images. | |
| * | |
| * @since 7.9.2 | |
| * @param int $device_id Device ID. | |
| * @param int $limit Optional limit for number of images. | |
| * @return array|WP_Error Image data or WP_Error on failure. | |
| */ | |
| public function get_device_images($device_id, $limit = 0) | |
| { | |
| $params = array(); | |
| if ($limit > 0) { | |
| $params['limit'] = absint($limit); | |
| } | |
| return $this->make_request('devices/' . absint($device_id) . '/images/', $params); | |
| } | |
| /** | |
| * Get all manufacturers. | |
| * | |
| * @since 7.9.2 | |
| * @return array|WP_Error Manufacturers list or WP_Error on failure. | |
| */ | |
| public function get_manufacturers() | |
| { | |
| return $this->make_request('manufacturers/'); | |
| } | |
| /** | |
| * Get manufacturer by ID. | |
| * | |
| * @since 7.9.2 | |
| * @param int $manufacturer_id Manufacturer ID. | |
| * @return array|WP_Error Manufacturer data or WP_Error on failure. | |
| */ | |
| public function get_manufacturer($manufacturer_id) | |
| { | |
| return $this->make_request('manufacturers/' . absint($manufacturer_id) . '/'); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * File: class-idb-spec.php | |
| * Author: Krafty Sprouts Media, LLC | |
| * Created: 11/01/2026 | |
| * Last Modified: 13/01/2026 | |
| * Description: Spec content type handler for IDB plugin. | |
| * | |
| * @package IDB | |
| * @subpackage IDB/includes/modules/technology | |
| * @author Krafty Sprouts Media, LLC | |
| * @since 7.9.0 | |
| */ | |
| // If this file is called directly, abort. | |
| if (!defined('ABSPATH')) { | |
| exit; | |
| } | |
| /** | |
| * Spec class. | |
| * | |
| * Handles spec data and management. | |
| * | |
| * @since 7.9.0 | |
| */ | |
| class IDB_Spec | |
| { | |
| /** | |
| * Sync spec from MobileAPI.dev. | |
| * | |
| * Imports device data from MobileAPI.dev API and creates/updates WordPress post. | |
| * | |
| * @since 7.9.4 | |
| * @param int $device_id MobileAPI.dev device ID. | |
| * @return int|WP_Error Post ID on success, WP_Error on failure. | |
| */ | |
| public function sync_from_mobileapi($device_id) | |
| { | |
| // Prevent session expiration during long-running import | |
| // Increase execution time limit for import operations | |
| if (function_exists('set_time_limit')) { | |
| @set_time_limit(300); // 5 minutes for import | |
| } | |
| // Keep session alive during long operations | |
| if (session_status() === PHP_SESSION_ACTIVE) { | |
| @session_write_close(); // Close session to prevent locks | |
| } | |
| require_once IDB_PLUGIN_DIR . 'includes/api/class-idb-mobileapi-api.php'; | |
| $api = new IDB_MobileAPI_API(); | |
| if (!$api->is_configured()) { | |
| return new WP_Error('no_api_key', __('MobileAPI.dev API key not configured.', 'idb')); | |
| } | |
| // Check if device already exists (do this quickly before API calls) | |
| $existing_post_id = $this->get_post_by_mobileapi_id($device_id); | |
| if ($existing_post_id) { | |
| error_log('IDB: Device already exists (ID: ' . $existing_post_id . '), updating...'); | |
| } | |
| // Fetch complete device data with ALL specs and images in ONE request! | |
| // The /devices/{id}/ endpoint returns EVERYTHING: basic info + all 12 spec categories + images | |
| error_log('IDB MobileAPI: Fetching complete device data for ID ' . $device_id); | |
| $device_data = $api->get_device($device_id); | |
| if (is_wp_error($device_data)) { | |
| error_log('IDB MobileAPI: Error fetching device data - ' . $device_data->get_error_message()); | |
| return $device_data; | |
| } | |
| // Extract nested specs data from device response | |
| // DEBUG: Log what keys are actually in the device_data response | |
| error_log('IDB DEBUG: device_data keys: ' . implode(', ', array_keys($device_data))); | |
| // The API returns specs as nested objects: device_data['network'], device_data['battery'], etc. | |
| $specs_data = array(); | |
| $spec_categories = array('network', 'battery', 'display', 'platform', 'memory', 'main_camera', 'selfie_camera', 'body', 'sound', 'comms', 'features', 'misc'); | |
| foreach ($spec_categories as $category) { | |
| // Handle both underscore and hyphen naming (main_camera vs main-camera) | |
| $category_key = str_replace('_', '-', $category); | |
| $category_key_underscore = str_replace('-', '_', $category); | |
| if (isset($device_data[$category_key]) && !empty($device_data[$category_key])) { | |
| $specs_data[$category_key] = $device_data[$category_key]; | |
| error_log('IDB: ✅ Found ' . $category_key . ' specs - Keys: ' . implode(', ', array_keys($device_data[$category_key]))); | |
| } elseif (isset($device_data[$category_key_underscore]) && !empty($device_data[$category_key_underscore])) { | |
| $specs_data[$category_key] = $device_data[$category_key_underscore]; | |
| error_log('IDB: ✅ Found ' . $category_key_underscore . ' specs - Keys: ' . implode(', ', array_keys($device_data[$category_key_underscore]))); | |
| } else { | |
| error_log('IDB: ❌ MISSING ' . $category_key . ' specs in device response'); | |
| } | |
| } | |
| // Extract images from device response | |
| // The API returns images in the 'images' key as an array | |
| $images_data = array(); | |
| if (isset($device_data['images']) && is_array($device_data['images']) && !empty($device_data['images'])) { | |
| $images_data = $device_data['images']; | |
| error_log('IDB: Found ' . count($images_data) . ' images in device response'); | |
| } else { | |
| error_log('IDB: No images array found in device response, will use main_image_b64 as fallback'); | |
| } | |
| // Log summary | |
| $specs_found = count($specs_data); | |
| $images_found = count($images_data); | |
| error_log('IDB MobileAPI: Device data received - Specs found: ' . $specs_found . '/12, Images found: ' . $images_found); | |
| // Generate excerpt/description if not provided by API | |
| $excerpt = $device_data['description'] ?? ''; | |
| if (empty($excerpt)) { | |
| $excerpt = $this->generate_device_description($device_data, $specs_data); | |
| } | |
| // Create or update WordPress post. | |
| $post_data = array( | |
| 'post_title' => $device_data['name'] ?? '', | |
| 'post_excerpt' => $excerpt, | |
| 'post_content' => '', // Will be filled by AI. | |
| 'post_status' => 'draft', // Save as draft first, not published immediately. | |
| 'post_type' => 'idb_spec', | |
| ); | |
| // Create or update WordPress post | |
| if ($existing_post_id) { | |
| $post_data['ID'] = $existing_post_id; | |
| $post_id = wp_update_post($post_data); | |
| } else { | |
| $post_id = wp_insert_post($post_data); | |
| } | |
| if (is_wp_error($post_id)) { | |
| return $post_id; | |
| } | |
| // Save meta fields. | |
| $this->save_meta_fields($post_id, $device_data, $specs_data); | |
| // Set taxonomies. | |
| $this->set_taxonomies($post_id, $device_data, $specs_data); | |
| // Download and process images. | |
| $this->download_images($post_id, $images_data, $device_data); | |
| // Generate AI content after all data is saved (7.12.1). | |
| if (class_exists('IDB_AI_Service')) { | |
| $ai_service = new IDB_AI_Service(); | |
| // Prepare comprehensive data for AI. | |
| $ai_data = $this->prepare_ai_data($post_id, $device_data, $specs_data); | |
| // Generate AI content (async, non-blocking). | |
| $ai_service->generate_content_on_import($post_id, 'idb_spec', $ai_data); | |
| } | |
| // Log final summary | |
| $specs_summary = count($specs_data) . '/12 spec categories'; | |
| $images_summary = !empty($images_data) ? count($images_data) . ' images' : '0 images (used main_image_b64 fallback)'; | |
| error_log('IDB: ✅ Successfully imported device ID ' . $device_id . ' as post ID ' . $post_id . ' - ' . $specs_summary . ', ' . $images_summary . ' (1 API request total)'); | |
| return $post_id; | |
| } | |
| /** | |
| * Get post by MobileAPI.dev ID. | |
| * | |
| * @since 7.9.4 | |
| * @param int $mobileapi_id MobileAPI.dev device ID. | |
| * @return int|false Post ID or false if not found. | |
| */ | |
| private function get_post_by_mobileapi_id($mobileapi_id) | |
| { | |
| $posts = get_posts(array( | |
| 'post_type' => 'idb_spec', | |
| 'meta_key' => '_idb_mobileapi_id', | |
| 'meta_value' => $mobileapi_id, | |
| 'posts_per_page' => 1, | |
| 'fields' => 'ids', | |
| )); | |
| return !empty($posts) ? $posts[0] : false; | |
| } | |
| /** | |
| * Get spec data for display. | |
| * | |
| * Returns all spec-specific fields from post meta and taxonomies. | |
| * | |
| * @since 7.9.0 | |
| * @param int $post_id Post ID. | |
| * @return array Spec data. | |
| */ | |
| public function get_data($post_id) | |
| { | |
| return array( | |
| // Basic info. | |
| 'title' => get_the_title($post_id), | |
| 'description' => get_the_excerpt($post_id), | |
| 'excerpt' => get_the_excerpt($post_id), | |
| 'content' => get_the_content(null, false, $post_id), // AI-generated content. | |
| // Identifiers. | |
| 'model' => get_post_meta($post_id, '_idb_model', true), // Model numbers from MobileAPI (was _idb_sku). | |
| 'mobileapi_id' => get_post_meta($post_id, '_idb_mobileapi_id', true), | |
| // Basic Info. | |
| 'release_date' => get_post_meta($post_id, '_idb_release_date', true), | |
| 'price' => get_post_meta($post_id, '_idb_price', true), | |
| 'currency' => get_post_meta($post_id, '_idb_currency', true), | |
| 'sar_us' => get_post_meta($post_id, '_idb_sar_us', true), | |
| 'sar_eu' => get_post_meta($post_id, '_idb_sar_eu', true), | |
| // Display Specs. | |
| 'screen_size' => get_post_meta($post_id, '_idb_screen_size', true), | |
| 'resolution' => get_post_meta($post_id, '_idb_resolution', true), | |
| 'display_type' => get_post_meta($post_id, '_idb_display_type', true), | |
| 'refresh_rate' => get_post_meta($post_id, '_idb_refresh_rate', true), | |
| 'pixel_density' => get_post_meta($post_id, '_idb_pixel_density', true), | |
| 'display_protection' => get_post_meta($post_id, '_idb_display_protection', true), | |
| 'display_other' => get_post_meta($post_id, '_idb_display_other', true), | |
| // Performance Specs. | |
| 'chipset' => get_post_meta($post_id, '_idb_chipset', true), | |
| 'cpu' => get_post_meta($post_id, '_idb_cpu', true), | |
| 'gpu' => get_post_meta($post_id, '_idb_gpu', true), | |
| 'ram' => get_post_meta($post_id, '_idb_ram', true), | |
| 'storage' => get_post_meta($post_id, '_idb_storage', true), | |
| 'card_slot' => get_post_meta($post_id, '_idb_card_slot', true), | |
| 'memory_other' => get_post_meta($post_id, '_idb_memory_other', true), | |
| 'os' => get_post_meta($post_id, '_idb_os', true), | |
| 'os_version' => get_post_meta($post_id, '_idb_os_version', true), | |
| // Camera Specs. | |
| 'camera_rear' => get_post_meta($post_id, '_idb_camera_rear', true), | |
| 'camera_front' => get_post_meta($post_id, '_idb_camera_front', true), | |
| 'camera_front_features' => get_post_meta($post_id, '_idb_camera_front_features', true), | |
| 'camera_front_video' => get_post_meta($post_id, '_idb_camera_front_video', true), | |
| 'video_recording' => get_post_meta($post_id, '_idb_video_recording', true), | |
| 'camera_features' => get_post_meta($post_id, '_idb_camera_features', true), | |
| // Battery Specs. | |
| 'battery_capacity' => get_post_meta($post_id, '_idb_battery_capacity', true), | |
| 'charging_speed' => get_post_meta($post_id, '_idb_charging_speed', true), | |
| 'wireless_charging' => get_post_meta($post_id, '_idb_wireless_charging', true), | |
| 'battery_type' => get_post_meta($post_id, '_idb_battery_type', true), | |
| // Design Specs. | |
| 'dimensions' => get_post_meta($post_id, '_idb_dimensions', true), | |
| 'weight' => get_post_meta($post_id, '_idb_weight', true), | |
| 'build_materials' => get_post_meta($post_id, '_idb_build_materials', true), | |
| 'body_other' => get_post_meta($post_id, '_idb_body_other', true), | |
| 'colors' => get_post_meta($post_id, '_idb_colors', true), | |
| 'water_resistance' => get_post_meta($post_id, '_idb_water_resistance', true), | |
| // Connectivity Specs. | |
| 'network' => get_post_meta($post_id, '_idb_network', true), | |
| 'network_speed' => get_post_meta($post_id, '_idb_network_speed', true), | |
| 'wifi' => get_post_meta($post_id, '_idb_wifi', true), | |
| 'bluetooth' => get_post_meta($post_id, '_idb_bluetooth', true), | |
| 'usb' => get_post_meta($post_id, '_idb_usb', true), | |
| 'nfc' => get_post_meta($post_id, '_idb_nfc', true), | |
| 'gps' => get_post_meta($post_id, '_idb_gps', true), | |
| 'radio' => get_post_meta($post_id, '_idb_radio', true), | |
| // Other Specs. | |
| 'sensors' => get_post_meta($post_id, '_idb_sensors', true), | |
| 'audio' => get_post_meta($post_id, '_idb_audio', true), | |
| 'sim' => get_post_meta($post_id, '_idb_sim', true), | |
| // Product Features. | |
| 'features' => get_post_meta($post_id, '_idb_features', true), | |
| 'features_other' => get_post_meta($post_id, '_idb_features_other', true), | |
| // Note: Pros/Cons are now generated by AI as part of the editorial content (## Pros & Cons section) | |
| // Removed separate pros/cons meta fields - they're included in the AI-generated content | |
| // Media. | |
| 'gallery' => get_post_meta($post_id, '_idb_gallery', true), | |
| // Affiliate/Store Links. | |
| 'store_links' => get_post_meta($post_id, '_idb_store_links', true), | |
| // Ratings. | |
| 'rating_average' => get_post_meta($post_id, '_idb_rating_average', true), | |
| 'rating_count' => get_post_meta($post_id, '_idb_rating_count', true), | |
| // Status. | |
| 'status' => get_post_meta($post_id, 'status', true), | |
| // Taxonomies. | |
| 'brand' => wp_get_post_terms($post_id, 'idb_brand', array('fields' => 'all')), | |
| 'product_category' => wp_get_post_terms($post_id, 'idb_product_category', array('fields' => 'all')), | |
| 'os_type' => wp_get_post_terms($post_id, 'idb_os_type', array('fields' => 'all')), | |
| 'status_tax' => wp_get_post_terms($post_id, 'idb_status', array('fields' => 'all')), | |
| // Category-based arrays for detailed specs display (reconstructed from individual meta fields). | |
| 'display' => array_filter(array( | |
| 'size' => get_post_meta($post_id, '_idb_screen_size', true), | |
| 'resolution' => get_post_meta($post_id, '_idb_resolution', true), | |
| 'type' => get_post_meta($post_id, '_idb_display_type', true), | |
| 'refresh_rate' => get_post_meta($post_id, '_idb_refresh_rate', true), | |
| 'pixel_density' => get_post_meta($post_id, '_idb_pixel_density', true), | |
| 'protection' => get_post_meta($post_id, '_idb_display_protection', true), | |
| 'other' => get_post_meta($post_id, '_idb_display_other', true), | |
| )), | |
| 'platform' => array_filter(array( | |
| 'chipset' => get_post_meta($post_id, '_idb_chipset', true), | |
| 'cpu' => get_post_meta($post_id, '_idb_cpu', true), | |
| 'gpu' => get_post_meta($post_id, '_idb_gpu', true), | |
| 'os' => get_post_meta($post_id, '_idb_os', true), | |
| 'os_version' => get_post_meta($post_id, '_idb_os_version', true), | |
| )), | |
| 'memory' => array_filter(array( | |
| 'ram' => get_post_meta($post_id, '_idb_ram', true), | |
| 'storage' => get_post_meta($post_id, '_idb_storage', true), | |
| 'card_slot' => get_post_meta($post_id, '_idb_card_slot', true), | |
| 'other' => get_post_meta($post_id, '_idb_memory_other', true), | |
| )), | |
| 'main_camera' => array_filter(array( | |
| 'modules' => get_post_meta($post_id, '_idb_camera_rear', true), | |
| 'video' => get_post_meta($post_id, '_idb_video_recording', true), | |
| 'features' => get_post_meta($post_id, '_idb_camera_features', true), | |
| )), | |
| 'selfie_camera' => array_filter(array( | |
| 'modules' => get_post_meta($post_id, '_idb_camera_front', true), | |
| 'video' => get_post_meta($post_id, '_idb_camera_front_video', true), | |
| 'features' => get_post_meta($post_id, '_idb_camera_front_features', true), | |
| )), | |
| 'battery' => array_filter(array( | |
| 'capacity' => get_post_meta($post_id, '_idb_battery_capacity', true), | |
| 'type' => get_post_meta($post_id, '_idb_battery_type', true), | |
| 'charging' => get_post_meta($post_id, '_idb_charging_speed', true), | |
| 'wireless_charging' => get_post_meta($post_id, '_idb_wireless_charging', true) ? 'Yes' : '', | |
| )), | |
| 'body' => array_filter(array( | |
| 'dimensions' => get_post_meta($post_id, '_idb_dimensions', true), | |
| 'weight' => get_post_meta($post_id, '_idb_weight', true), | |
| 'build' => get_post_meta($post_id, '_idb_build_materials', true), | |
| 'water_resistance' => get_post_meta($post_id, '_idb_water_resistance', true), | |
| 'sim' => get_post_meta($post_id, '_idb_sim', true), | |
| 'other' => get_post_meta($post_id, '_idb_body_other', true), | |
| )), | |
| 'sound' => array_filter(array( | |
| 'loudspeaker' => get_post_meta($post_id, '_idb_audio', true), | |
| 'radio' => get_post_meta($post_id, '_idb_radio', true), | |
| )), | |
| 'comms' => array_filter(array( | |
| 'wlan' => get_post_meta($post_id, '_idb_wifi', true), | |
| 'bluetooth' => get_post_meta($post_id, '_idb_bluetooth', true), | |
| 'usb' => get_post_meta($post_id, '_idb_usb', true), | |
| 'nfc' => get_post_meta($post_id, '_idb_nfc', true) ? 'Yes' : '', | |
| 'positioning' => get_post_meta($post_id, '_idb_gps', true), | |
| 'radio' => get_post_meta($post_id, '_idb_radio', true), | |
| )), | |
| 'network' => array_filter(array( | |
| 'technology' => get_post_meta($post_id, '_idb_network', true), | |
| 'speed' => get_post_meta($post_id, '_idb_network_speed', true), | |
| )), | |
| 'features' => array_filter(array( | |
| 'sensors' => get_post_meta($post_id, '_idb_sensors', true), | |
| 'other_features' => get_post_meta($post_id, '_idb_features', true), | |
| 'other' => get_post_meta($post_id, '_idb_features_other', true), | |
| )), | |
| 'misc' => array_filter(array( | |
| 'colors' => get_post_meta($post_id, '_idb_colors', true), | |
| 'price' => get_post_meta($post_id, '_idb_price', true), | |
| 'currency' => get_post_meta($post_id, '_idb_currency', true), | |
| 'sar_us' => get_post_meta($post_id, '_idb_sar_us', true), | |
| 'sar_eu' => get_post_meta($post_id, '_idb_sar_eu', true), | |
| )), | |
| ); | |
| } | |
| /** | |
| * Save meta fields from MobileAPI.dev data. | |
| * | |
| * @since 7.9.4 | |
| * @param int $post_id Post ID. | |
| * @param array $device_data Device data from MobileAPI.dev. | |
| * @param array $specs_data Specification data from all endpoints. | |
| * @return void | |
| */ | |
| private function save_meta_fields($post_id, $device_data, $specs_data) | |
| { | |
| // Store MobileAPI.dev ID. | |
| update_post_meta($post_id, '_idb_mobileapi_id', $device_data['id'] ?? ''); | |
| // Basic Info. | |
| update_post_meta($post_id, '_idb_model', $this->parse_model_numbers($device_data['model_numbers'] ?? '')); // Model numbers from manufacturer. | |
| update_post_meta($post_id, '_idb_release_date', $this->parse_release_date($device_data['release_date'] ?? '')); | |
| update_post_meta($post_id, '_idb_colors', $this->parse_comma_separated($device_data['colors'] ?? '')); | |
| // Quick specs from main device endpoint. | |
| if (!empty($device_data['screen_resolution'])) { | |
| update_post_meta($post_id, '_idb_resolution', $this->parse_resolution($device_data['screen_resolution'])); | |
| } | |
| if (!empty($device_data['camera'])) { | |
| update_post_meta($post_id, '_idb_camera_rear', $device_data['camera']); | |
| } | |
| if (!empty($device_data['hardware'])) { | |
| update_post_meta($post_id, '_idb_chipset', $device_data['hardware']); | |
| } | |
| if (!empty($device_data['battery_capacity'])) { | |
| update_post_meta($post_id, '_idb_battery_capacity', $this->extract_number($device_data['battery_capacity'])); | |
| } | |
| if (!empty($device_data['storage'])) { | |
| update_post_meta($post_id, '_idb_storage', $this->parse_storage($device_data['storage'])); | |
| } | |
| if (!empty($device_data['weight'])) { | |
| update_post_meta($post_id, '_idb_weight', $this->extract_number($device_data['weight'])); | |
| } | |
| // Network specs. | |
| if (!empty($specs_data['network'])) { | |
| $network = $specs_data['network']; | |
| $network_str = ''; | |
| if (!empty($network['technology'])) { | |
| $network_str = $network['technology']; | |
| } | |
| if (!empty($network['bands']) && is_array($network['bands'])) { | |
| $network_str .= ($network_str ? ', ' : '') . implode(', ', $network['bands']); | |
| } | |
| if ($network_str) { | |
| update_post_meta($post_id, '_idb_network', $network_str); | |
| } | |
| // Network speed (e.g., "HSPA, LTE-A, 5G") | |
| if (!empty($network['speed'])) { | |
| update_post_meta($post_id, '_idb_network_speed', $network['speed']); | |
| } | |
| } | |
| // Battery specs. | |
| if (!empty($specs_data['battery'])) { | |
| $battery = $specs_data['battery']; | |
| if (!empty($battery['type'])) { | |
| $battery_type = $this->extract_battery_type($battery['type']); | |
| update_post_meta($post_id, '_idb_battery_type', $battery_type); | |
| } | |
| if (!empty($battery['capacity'])) { | |
| update_post_meta($post_id, '_idb_battery_capacity', $this->extract_number($battery['capacity'])); | |
| } | |
| if (!empty($battery['charging'])) { | |
| $charging_speed = $this->extract_charging_speed($battery['charging']); | |
| if ($charging_speed) { | |
| update_post_meta($post_id, '_idb_charging_speed', $charging_speed); | |
| } | |
| } | |
| if (!empty($battery['wireless_charging'])) { | |
| update_post_meta($post_id, '_idb_wireless_charging', '1'); | |
| } else { | |
| delete_post_meta($post_id, '_idb_wireless_charging'); | |
| } | |
| } | |
| // Display specs. | |
| if (!empty($specs_data['display'])) { | |
| $display = $specs_data['display']; | |
| if (!empty($display['size'])) { | |
| update_post_meta($post_id, '_idb_screen_size', $this->extract_number($display['size'])); | |
| } | |
| if (!empty($display['resolution'])) { | |
| update_post_meta($post_id, '_idb_resolution', $this->parse_resolution($display['resolution'])); | |
| } | |
| if (!empty($display['type'])) { | |
| update_post_meta($post_id, '_idb_display_type', $display['type']); | |
| } | |
| if (!empty($display['refresh_rate'])) { | |
| update_post_meta($post_id, '_idb_refresh_rate', $this->extract_number($display['refresh_rate'])); | |
| } | |
| if (!empty($display['pixel_density'])) { | |
| update_post_meta($post_id, '_idb_pixel_density', $this->extract_number($display['pixel_density'])); | |
| } | |
| // Display protection (e.g., "Gorilla Glass Victus") | |
| if (!empty($display['protection'])) { | |
| update_post_meta($post_id, '_idb_display_protection', $display['protection']); | |
| } | |
| // Display other features (catch-all) | |
| if (!empty($display['other'])) { | |
| update_post_meta($post_id, '_idb_display_other', is_array($display['other']) ? implode(', ', $display['other']) : $display['other']); | |
| } | |
| } | |
| // Platform specs. | |
| if (!empty($specs_data['platform'])) { | |
| $platform = $specs_data['platform']; | |
| if (!empty($platform['chipset'])) { | |
| update_post_meta($post_id, '_idb_chipset', $platform['chipset']); | |
| } | |
| if (!empty($platform['cpu'])) { | |
| update_post_meta($post_id, '_idb_cpu', $platform['cpu']); | |
| } | |
| if (!empty($platform['gpu'])) { | |
| update_post_meta($post_id, '_idb_gpu', $platform['gpu']); | |
| } | |
| if (!empty($platform['os'])) { | |
| update_post_meta($post_id, '_idb_os', $platform['os']); | |
| } | |
| if (!empty($platform['os_version'])) { | |
| update_post_meta($post_id, '_idb_os_version', $platform['os_version']); | |
| } | |
| } | |
| // Memory specs. | |
| if (!empty($specs_data['memory'])) { | |
| $memory = $specs_data['memory']; | |
| if (!empty($memory['ram'])) { | |
| update_post_meta($post_id, '_idb_ram', $this->extract_number($memory['ram'])); | |
| } | |
| if (!empty($memory['storage'])) { | |
| update_post_meta($post_id, '_idb_storage', $this->parse_storage($memory['storage'])); | |
| } | |
| // Card slot (e.g., "microSD up to 1TB") | |
| if (!empty($memory['card_slot'])) { | |
| update_post_meta($post_id, '_idb_card_slot', $memory['card_slot']); | |
| } | |
| // Memory other features (catch-all) | |
| if (!empty($memory['other'])) { | |
| update_post_meta($post_id, '_idb_memory_other', is_array($memory['other']) ? implode(', ', $memory['other']) : $memory['other']); | |
| } | |
| } | |
| // Main camera specs. | |
| if (!empty($specs_data['main-camera'])) { | |
| $camera = $specs_data['main-camera']; | |
| error_log('IDB DEBUG: Main Camera data = ' . json_encode($camera)); | |
| // API returns 'modules' not 'rear_camera' | |
| if (!empty($camera['modules'])) { | |
| update_post_meta($post_id, '_idb_camera_rear', $camera['modules']); | |
| error_log('IDB: ✅ Saved main camera modules'); | |
| } else { | |
| error_log('IDB: ❌ Main camera modules field is EMPTY'); | |
| } | |
| // API returns 'video' not 'video_recording' | |
| if (!empty($camera['video'])) { | |
| update_post_meta($post_id, '_idb_video_recording', $camera['video']); | |
| error_log('IDB: ✅ Saved main camera video'); | |
| } else { | |
| error_log('IDB: ❌ Main camera video field is EMPTY'); | |
| } | |
| if (!empty($camera['features'])) { | |
| update_post_meta($post_id, '_idb_camera_features', is_array($camera['features']) ? implode(', ', $camera['features']) : $camera['features']); | |
| } | |
| } else { | |
| error_log('IDB: ❌ NO MAIN CAMERA DATA IN specs_data'); | |
| } | |
| // Selfie camera specs. | |
| if (!empty($specs_data['selfie-camera'])) { | |
| $camera = $specs_data['selfie-camera']; | |
| // API returns 'modules' not 'front_camera' | |
| if (!empty($camera['modules'])) { | |
| update_post_meta($post_id, '_idb_camera_front', $camera['modules']); | |
| } | |
| // Selfie camera features | |
| if (!empty($camera['features'])) { | |
| update_post_meta($post_id, '_idb_camera_front_features', is_array($camera['features']) ? implode(', ', $camera['features']) : $camera['features']); | |
| } | |
| // Selfie camera video | |
| if (!empty($camera['video'])) { | |
| update_post_meta($post_id, '_idb_camera_front_video', $camera['video']); | |
| } | |
| } | |
| // Body specs. | |
| if (!empty($specs_data['body'])) { | |
| $body = $specs_data['body']; | |
| if (!empty($body['dimensions'])) { | |
| update_post_meta($post_id, '_idb_dimensions', $body['dimensions']); | |
| } | |
| if (!empty($body['weight'])) { | |
| update_post_meta($post_id, '_idb_weight', $this->extract_number($body['weight'])); | |
| } | |
| if (!empty($body['build'])) { | |
| update_post_meta($post_id, '_idb_build_materials', $body['build']); | |
| } | |
| if (!empty($body['water_resistance'])) { | |
| update_post_meta($post_id, '_idb_water_resistance', $body['water_resistance']); | |
| } | |
| if (!empty($body['sim'])) { | |
| update_post_meta($post_id, '_idb_sim', $body['sim']); | |
| } | |
| // Body other features (catch-all) | |
| if (!empty($body['other'])) { | |
| update_post_meta($post_id, '_idb_body_other', is_array($body['other']) ? implode(', ', $body['other']) : $body['other']); | |
| } | |
| } | |
| // Sound specs. | |
| if (!empty($specs_data['sound'])) { | |
| $sound = $specs_data['sound']; | |
| $audio_parts = array(); | |
| // API returns 'loudspeaker' not 'speakers' | |
| if (!empty($sound['loudspeaker'])) { | |
| $audio_parts[] = $sound['loudspeaker']; | |
| } | |
| if (!empty($sound['audio_jack'])) { | |
| $audio_parts[] = $sound['audio_jack']; | |
| } | |
| if (!empty($audio_parts)) { | |
| update_post_meta($post_id, '_idb_audio', implode(', ', $audio_parts)); | |
| } | |
| // Also save radio if available (might be in sound or comms) | |
| if (!empty($sound['radio'])) { | |
| update_post_meta($post_id, '_idb_radio', $sound['radio']); | |
| } | |
| } | |
| // Comms specs. | |
| if (!empty($specs_data['comms'])) { | |
| $comms = $specs_data['comms']; | |
| error_log('IDB DEBUG: Comms data = ' . json_encode($comms)); | |
| // API returns 'wlan' not 'wifi' | |
| if (!empty($comms['wlan'])) { | |
| update_post_meta($post_id, '_idb_wifi', $comms['wlan']); | |
| error_log('IDB: ✅ Saved WiFi'); | |
| } else { | |
| error_log('IDB: ❌ WiFi (wlan) field is EMPTY'); | |
| } | |
| if (!empty($comms['bluetooth'])) { | |
| update_post_meta($post_id, '_idb_bluetooth', $comms['bluetooth']); | |
| error_log('IDB: ✅ Saved Bluetooth'); | |
| } else { | |
| error_log('IDB: ❌ Bluetooth field is EMPTY'); | |
| } | |
| if (!empty($comms['usb'])) { | |
| update_post_meta($post_id, '_idb_usb', $comms['usb']); | |
| } | |
| if (!empty($comms['nfc'])) { | |
| update_post_meta($post_id, '_idb_nfc', $comms['nfc'] === true || $comms['nfc'] === '1' ? '1' : ''); | |
| } else { | |
| delete_post_meta($post_id, '_idb_nfc'); | |
| } | |
| // API returns 'positioning' not 'gps' | |
| if (!empty($comms['positioning'])) { | |
| update_post_meta($post_id, '_idb_gps', $comms['positioning']); | |
| error_log('IDB: ✅ Saved GPS'); | |
| } else { | |
| error_log('IDB: ❌ GPS (positioning) field is EMPTY'); | |
| delete_post_meta($post_id, '_idb_gps'); | |
| } | |
| // Also save radio if available | |
| if (!empty($comms['radio'])) { | |
| update_post_meta($post_id, '_idb_radio', $comms['radio']); | |
| } | |
| } else { | |
| error_log('IDB: ❌ NO COMMS DATA IN specs_data'); | |
| } | |
| // Features specs. | |
| if (!empty($specs_data['features'])) { | |
| $features = $specs_data['features']; | |
| if (!empty($features['sensors'])) { | |
| update_post_meta($post_id, '_idb_sensors', is_array($features['sensors']) ? implode(', ', $features['sensors']) : $features['sensors']); | |
| } | |
| if (!empty($features['other_features'])) { | |
| update_post_meta($post_id, '_idb_features', is_array($features['other_features']) ? json_encode($features['other_features']) : $features['other_features']); | |
| } | |
| // Features other (catch-all) | |
| if (!empty($features['other'])) { | |
| update_post_meta($post_id, '_idb_features_other', is_array($features['other']) ? implode(', ', $features['other']) : $features['other']); | |
| } | |
| } | |
| // Misc specs. | |
| if (!empty($specs_data['misc'])) { | |
| $misc = $specs_data['misc']; | |
| if (!empty($misc['colors'])) { | |
| update_post_meta($post_id, '_idb_colors', $this->parse_comma_separated($misc['colors'])); | |
| } | |
| if (!empty($misc['price'])) { | |
| update_post_meta($post_id, '_idb_price', $this->extract_number($misc['price'])); | |
| } | |
| if (!empty($misc['currency'])) { | |
| update_post_meta($post_id, '_idb_currency', $misc['currency']); | |
| } | |
| // SAR values (radiation) | |
| if (!empty($misc['sar_us'])) { | |
| update_post_meta($post_id, '_idb_sar_us', $misc['sar_us']); | |
| } | |
| if (!empty($misc['sar_eu'])) { | |
| update_post_meta($post_id, '_idb_sar_eu', $misc['sar_eu']); | |
| } | |
| } | |
| } | |
| /** | |
| * Set taxonomies from MobileAPI.dev data. | |
| * | |
| * @since 7.9.4 | |
| * @param int $post_id Post ID. | |
| * @param array $device_data Device data from MobileAPI.dev. | |
| * @param array $specs_data Specification data. | |
| * @return void | |
| */ | |
| private function set_taxonomies($post_id, $device_data, $specs_data) | |
| { | |
| // Brand taxonomy. | |
| $brand_name = $device_data['brand']['name'] ?? $device_data['manufacturer_name'] ?? ''; | |
| if (!empty($brand_name)) { | |
| $brand_term = get_term_by('name', $brand_name, 'idb_brand'); | |
| if (!$brand_term) { | |
| $term_result = wp_insert_term($brand_name, 'idb_brand'); | |
| if (!is_wp_error($term_result)) { | |
| wp_set_post_terms($post_id, array($term_result['term_id']), 'idb_brand'); | |
| } | |
| } else { | |
| wp_set_post_terms($post_id, array($brand_term->term_id), 'idb_brand'); | |
| } | |
| } | |
| // Product Category taxonomy. | |
| $device_type = strtolower($device_data['device_type'] ?? ''); | |
| if (!empty($device_type)) { | |
| error_log('IDB DEBUG: Device type from API: "' . $device_type . '" for device: ' . ($device_data['name'] ?? 'unknown')); | |
| $category_map = array( | |
| 'phone' => 'Phones', | |
| 'tablet' => 'Tablets', | |
| 'laptop' => 'Laptops', | |
| 'wearable' => 'Wearables', | |
| 'other' => 'Other', | |
| ); | |
| $category_name = $category_map[$device_type] ?? ucfirst($device_type); | |
| error_log('IDB DEBUG: Mapped to category: "' . $category_name . '"'); | |
| $category_term = get_term_by('name', $category_name, 'idb_product_category'); | |
| if (!$category_term) { | |
| $term_result = wp_insert_term($category_name, 'idb_product_category'); | |
| if (!is_wp_error($term_result)) { | |
| wp_set_post_terms($post_id, array($term_result['term_id']), 'idb_product_category'); | |
| } | |
| } else { | |
| wp_set_post_terms($post_id, array($category_term->term_id), 'idb_product_category'); | |
| } | |
| } | |
| // OS Type taxonomy. | |
| $os = $specs_data['platform']['os'] ?? ''; | |
| if (!empty($os)) { | |
| $os_term = get_term_by('name', $os, 'idb_os_type'); | |
| if (!$os_term) { | |
| $term_result = wp_insert_term($os, 'idb_os_type'); | |
| if (!is_wp_error($term_result)) { | |
| wp_set_post_terms($post_id, array($term_result['term_id']), 'idb_os_type'); | |
| } | |
| } else { | |
| wp_set_post_terms($post_id, array($os_term->term_id), 'idb_os_type'); | |
| } | |
| } | |
| // Status taxonomy. | |
| $release_date = $this->parse_release_date($device_data['release_date'] ?? ''); | |
| $status = 'Available'; | |
| if (!empty($release_date)) { | |
| $release_timestamp = strtotime($release_date); | |
| if ($release_timestamp && $release_timestamp > time()) { | |
| $status = 'Upcoming'; | |
| } | |
| } | |
| $status_term = get_term_by('name', $status, 'idb_status'); | |
| if (!$status_term) { | |
| $term_result = wp_insert_term($status, 'idb_status'); | |
| if (!is_wp_error($term_result)) { | |
| wp_set_post_terms($post_id, array($term_result['term_id']), 'idb_status'); | |
| } | |
| } else { | |
| wp_set_post_terms($post_id, array($status_term->term_id), 'idb_status'); | |
| } | |
| update_post_meta($post_id, 'status', $status); | |
| } | |
| /** | |
| * Download and process images from MobileAPI.dev. | |
| * | |
| * @since 7.9.4 | |
| * @param int $post_id Post ID. | |
| * @param array $images_data Images data from MobileAPI.dev. | |
| * @param array $device_data Device data. | |
| * @return void | |
| */ | |
| private function download_images($post_id, $images_data, $device_data) | |
| { | |
| require_once(ABSPATH . 'wp-admin/includes/file.php'); | |
| require_once(ABSPATH . 'wp-admin/includes/media.php'); | |
| require_once(ABSPATH . 'wp-admin/includes/image.php'); | |
| $gallery_images = array(); | |
| $main_image_set = false; | |
| // Process images from images endpoint if available. | |
| if (!empty($images_data) && is_array($images_data)) { | |
| error_log('IDB: Processing ' . count($images_data) . ' images from images endpoint for post ID ' . $post_id); | |
| // DEBUG: Log each image's metadata | |
| foreach ($images_data as $idx => $img_meta) { | |
| $caption = $img_meta['caption'] ?? 'no caption'; | |
| $order = $img_meta['order'] ?? 'no order'; | |
| $is_official = isset($img_meta['is_official']) ? ($img_meta['is_official'] ? 'YES' : 'NO') : 'unknown'; | |
| $has_b64 = !empty($img_meta['image_b64']) ? 'YES (' . strlen($img_meta['image_b64']) . ' chars)' : 'NO'; | |
| error_log("IDB DEBUG: Image $idx - Caption: '$caption', Order: $order, Official: $is_official, Has B64: $has_b64"); | |
| } | |
| // Sort images by order (main image should be first). | |
| usort($images_data, function ($a, $b) { | |
| $order_a = $a['order'] ?? 999; | |
| $order_b = $b['order'] ?? 999; | |
| return $order_a <=> $order_b; | |
| }); | |
| foreach ($images_data as $image_data) { | |
| if (empty($image_data['image_b64'])) { | |
| continue; | |
| } | |
| // Decode base64 image. | |
| $image_data_decoded = base64_decode($image_data['image_b64']); | |
| if ($image_data_decoded === false) { | |
| error_log('IDB: Failed to decode base64 image for device ID ' . $post_id); | |
| continue; | |
| } | |
| // DEBUG: Check the actual size of decoded image | |
| $decoded_size = strlen($image_data_decoded); | |
| error_log('IDB DEBUG: Image ' . ($image_data['order'] ?? 0) . ' - Decoded size: ' . number_format($decoded_size) . ' bytes (' . round($decoded_size / 1024, 2) . ' KB)'); | |
| // Create temporary file. | |
| $temp_file = wp_tempnam('idb-mobileapi-'); | |
| if (!$temp_file) { | |
| continue; | |
| } | |
| file_put_contents($temp_file, $image_data_decoded); | |
| // Determine file extension from image data or default to jpg. | |
| $file_extension = 'jpg'; | |
| $mime_type = wp_check_filetype($temp_file); | |
| if (!empty($mime_type['ext'])) { | |
| $file_extension = $mime_type['ext']; | |
| } | |
| // Generate filename. | |
| $device_name = sanitize_file_name($device_data['name'] ?? 'device'); | |
| $order = $image_data['order'] ?? 0; | |
| $filename = $device_name . '-' . $order . '.' . $file_extension; | |
| // Use device name as caption/title instead of API caption (which may be wrong) | |
| $caption = $device_data['name'] ?? ''; | |
| if (!empty($order)) { | |
| $caption .= ' - Image ' . ($order + 1); | |
| } | |
| // Upload to WordPress Media Library. | |
| $file_array = array( | |
| 'name' => $filename, | |
| 'tmp_name' => $temp_file, | |
| ); | |
| $attachment_id = media_handle_sideload($file_array, $post_id, $caption); | |
| if (is_wp_error($attachment_id)) { | |
| error_log('IDB: Failed to upload image for device ID ' . $post_id . ': ' . $attachment_id->get_error_message()); | |
| @unlink($temp_file); | |
| continue; | |
| } | |
| // Force update attachment title and caption to ensure correct device name | |
| wp_update_post(array( | |
| 'ID' => $attachment_id, | |
| 'post_title' => $caption, | |
| 'post_excerpt' => $caption, | |
| 'post_content' => '', // Clear description to avoid API caption leaking through | |
| )); | |
| // Set alt text to device name | |
| update_post_meta($attachment_id, '_wp_attachment_image_alt', $device_data['name'] ?? ''); | |
| // Get image dimensions from WordPress attachment metadata. | |
| $image_metadata = wp_get_attachment_metadata($attachment_id); | |
| $image_width = $image_metadata['width'] ?? 0; | |
| $image_height = $image_metadata['height'] ?? 0; | |
| $image_size = $image_width . 'x' . $image_height; | |
| // Set main image as featured image (first image or order 0). | |
| if (!$main_image_set && ($order === 0 || empty($gallery_images))) { | |
| set_post_thumbnail($post_id, $attachment_id); | |
| $main_image_set = true; | |
| error_log('IDB: Set featured image for post ID ' . $post_id . ' - Size: ' . $image_size . ' (Width: ' . $image_width . 'px, Height: ' . $image_height . 'px)'); | |
| } | |
| // Add to gallery with dimensions. | |
| $gallery_images[] = array( | |
| 'attachment_id' => $attachment_id, | |
| 'caption' => $caption, // Our generated caption | |
| 'api_caption' => $image_data['caption'] ?? '', // Original API caption for reference | |
| 'order' => $order, | |
| 'is_official' => $image_data['is_official'] ?? false, | |
| 'width' => $image_width, | |
| 'height' => $image_height, | |
| 'size' => $image_size, | |
| ); | |
| } | |
| // Store gallery images in post meta. | |
| if (!empty($gallery_images)) { | |
| update_post_meta($post_id, '_idb_gallery', json_encode($gallery_images)); | |
| error_log('IDB: Successfully downloaded and stored ' . count($gallery_images) . ' images for post ID ' . $post_id); | |
| } else { | |
| error_log('IDB: No images were successfully downloaded from images endpoint for post ID ' . $post_id); | |
| } | |
| } | |
| // Fallback: Use main_image_b64 from device_data if no images were downloaded from images endpoint. | |
| if (empty($gallery_images)) { | |
| if (!empty($device_data['main_image_b64'])) { | |
| error_log('IDB: Attempting to use main_image_b64 fallback from device data for post ID ' . $post_id); | |
| $image_data_decoded = base64_decode($device_data['main_image_b64']); | |
| if ($image_data_decoded !== false) { | |
| $temp_file = wp_tempnam('idb-mobileapi-main-'); | |
| if ($temp_file) { | |
| file_put_contents($temp_file, $image_data_decoded); | |
| $device_name = sanitize_file_name($device_data['name'] ?? 'device'); | |
| $caption = ($device_data['name'] ?? 'device') . ' - Main Image'; | |
| $file_array = array( | |
| 'name' => $device_name . '-main.jpg', | |
| 'tmp_name' => $temp_file, | |
| ); | |
| $attachment_id = media_handle_sideload($file_array, $post_id, $caption); | |
| if (!is_wp_error($attachment_id)) { | |
| // Force update attachment title and caption | |
| wp_update_post(array( | |
| 'ID' => $attachment_id, | |
| 'post_title' => $caption, | |
| 'post_excerpt' => $caption, | |
| 'post_content' => '', | |
| )); | |
| update_post_meta($attachment_id, '_wp_attachment_image_alt', $device_data['name'] ?? ''); | |
| set_post_thumbnail($post_id, $attachment_id); | |
| // Get and log image dimensions. | |
| $image_metadata = wp_get_attachment_metadata($attachment_id); | |
| $image_width = $image_metadata['width'] ?? 0; | |
| $image_height = $image_metadata['height'] ?? 0; | |
| error_log('IDB: Set featured image from main_image_b64 for post ID ' . $post_id . ' - Size: ' . $image_width . 'x' . $image_height . ' (Width: ' . $image_width . 'px, Height: ' . $image_height . 'px)'); | |
| } else { | |
| error_log('IDB: Failed to upload main_image_b64 fallback image for post ID ' . $post_id . ': ' . $attachment_id->get_error_message()); | |
| } | |
| } else { | |
| error_log('IDB: Failed to create temp file for main_image_b64 fallback for post ID ' . $post_id); | |
| } | |
| } else { | |
| error_log('IDB: No main_image_b64 in device_data for post ID ' . $post_id . ' - device_data keys: ' . implode(', ', array_keys($device_data ?? array()))); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Extract number from string (e.g., "3274 mAh" → 3274). | |
| * | |
| * @since 7.9.4 | |
| * @param string $string String containing number. | |
| * @return int|string Extracted number or original string if no number found. | |
| */ | |
| private function extract_number($string) | |
| { | |
| if (preg_match('/(\d+(?:\.\d+)?)/', $string, $matches)) { | |
| return is_numeric($matches[1]) ? (strpos($matches[1], '.') !== false ? floatval($matches[1]) : intval($matches[1])) : $string; | |
| } | |
| return $string; | |
| } | |
| /** | |
| * Parse release date to WordPress format. | |
| * | |
| * @since 7.9.4 | |
| * @param string $date_string Date string from MobileAPI.dev. | |
| * @return string Formatted date (YYYY-MM-DD) or empty string. | |
| */ | |
| private function parse_release_date($date_string) | |
| { | |
| if (empty($date_string)) { | |
| return ''; | |
| } | |
| // Try to parse "2023-09-22" format. | |
| if (preg_match('/(\d{4}-\d{2}-\d{2})/', $date_string, $matches)) { | |
| return $matches[1]; | |
| } | |
| // Try to parse "Released 2023, September 22" format. | |
| if (preg_match('/(\d{4}).*?(\w+)\s+(\d+)/', $date_string, $matches)) { | |
| $year = $matches[1]; | |
| $month_name = $matches[2]; | |
| $day = $matches[3]; | |
| $month = date('m', strtotime($month_name . ' 1')); | |
| return sprintf('%s-%02d-%02d', $year, $month, intval($day)); | |
| } | |
| // Try strtotime as fallback. | |
| $timestamp = strtotime($date_string); | |
| if ($timestamp) { | |
| return date('Y-m-d', $timestamp); | |
| } | |
| return ''; | |
| } | |
| /** | |
| * Parse resolution string (e.g., "2556 x 1179" → "2556x1179"). | |
| * | |
| * @since 7.9.4 | |
| * @param string $resolution Resolution string. | |
| * @return string Parsed resolution. | |
| */ | |
| private function parse_resolution($resolution) | |
| { | |
| if (preg_match('/(\d+)\s*[x×]\s*(\d+)/i', $resolution, $matches)) { | |
| return $matches[1] . 'x' . $matches[2]; | |
| } | |
| return $resolution; | |
| } | |
| /** | |
| * Parse storage string (e.g., "128GB, 256GB, 512GB" → "128, 256, 512"). | |
| * | |
| * @since 7.9.4 | |
| * @param string $storage Storage string. | |
| * @return string Parsed storage. | |
| */ | |
| private function parse_storage($storage) | |
| { | |
| $values = array(); | |
| if (preg_match_all('/(\d+)\s*(?:GB|TB)/i', $storage, $matches)) { | |
| foreach ($matches[1] as $value) { | |
| // Convert TB to GB if needed. | |
| if (strpos($storage, $value . 'TB') !== false) { | |
| $values[] = intval($value) * 1024; | |
| } else { | |
| $values[] = intval($value); | |
| } | |
| } | |
| return implode(', ', $values); | |
| } | |
| return $storage; | |
| } | |
| /** | |
| * Parse comma-separated values. | |
| * | |
| * @since 7.9.4 | |
| * @param string $string Comma-separated string. | |
| * @return string Parsed string (trimmed values). | |
| */ | |
| private function parse_comma_separated($string) | |
| { | |
| if (empty($string)) { | |
| return ''; | |
| } | |
| $values = array_map('trim', explode(',', $string)); | |
| return implode(', ', $values); | |
| } | |
| /** | |
| * Parse model numbers. | |
| * | |
| * @since 7.9.4 | |
| * @param string $model_numbers Model numbers string. | |
| * @return string Parsed model numbers. | |
| */ | |
| private function parse_model_numbers($model_numbers) | |
| { | |
| return $this->parse_comma_separated($model_numbers); | |
| } | |
| /** | |
| * Extract battery type from string (e.g., "Li-Ion 3274 mAh" → "Li-Ion"). | |
| * | |
| * @since 7.9.4 | |
| * @param string $string Battery type string. | |
| * @return string Extracted battery type. | |
| */ | |
| private function extract_battery_type($string) | |
| { | |
| if (preg_match('/(Li-Ion|Li-Po|Li-Poly|NiMH|NiCd)/i', $string, $matches)) { | |
| return $matches[1]; | |
| } | |
| return $string; | |
| } | |
| /** | |
| * Extract charging speed from string (e.g., "50% in 30 min" or "15W" → 15 or 50). | |
| * | |
| * @since 7.9.4 | |
| * @param string $string Charging string. | |
| * @return int|string Extracted speed or empty string. | |
| */ | |
| private function extract_charging_speed($string) | |
| { | |
| // Try to extract wattage (e.g., "15W", "50W"). | |
| if (preg_match('/(\d+)\s*W/i', $string, $matches)) { | |
| return intval($matches[1]); | |
| } | |
| // Try to extract percentage (e.g., "50% in 30 min"). | |
| if (preg_match('/(\d+)%/i', $string, $matches)) { | |
| return intval($matches[1]); | |
| } | |
| return ''; | |
| } | |
| /** | |
| * Prepare comprehensive data for AI content generation. | |
| * | |
| * @since 7.12.2 | |
| * @param int $post_id Post ID. | |
| * @param array $device_data Raw device data from MobileAPI.dev. | |
| * @param array $specs_data Specs data from MobileAPI.dev. | |
| * @return array Formatted data for AI. | |
| */ | |
| private function prepare_ai_data($post_id, $device_data, $specs_data) | |
| { | |
| // Get brand name | |
| $manufacturer = ''; | |
| if (!empty($device_data['brand']['name'])) { | |
| $manufacturer = $device_data['brand']['name']; | |
| } elseif (!empty($device_data['manufacturer_name'])) { | |
| $manufacturer = $device_data['manufacturer_name']; | |
| } | |
| // Get device type | |
| $device_type = ''; | |
| if (!empty($device_data['device_type'])) { | |
| $device_type = ucfirst(strtolower($device_data['device_type'])); | |
| } | |
| // Get category | |
| $category = ''; | |
| $device_type_lower = strtolower($device_data['device_type'] ?? ''); | |
| $category_map = array( | |
| 'phone' => 'Phones', | |
| 'tablet' => 'Tablets', | |
| 'laptop' => 'Laptops', | |
| 'wearable' => 'Wearables', | |
| 'other' => 'Other', | |
| ); | |
| $category = $category_map[$device_type_lower] ?? ucfirst($device_type_lower); | |
| // Get release date and year | |
| $release_date = $this->parse_release_date($device_data['release_date'] ?? ''); | |
| $release_year = ''; | |
| if (!empty($release_date)) { | |
| $release_year = date('Y', strtotime($release_date)); | |
| } | |
| // Determine status | |
| $status = 'Available'; | |
| if (!empty($release_date)) { | |
| $release_timestamp = strtotime($release_date); | |
| if ($release_timestamp && $release_timestamp > time()) { | |
| $status = 'Upcoming'; | |
| } | |
| } | |
| // Extract key specs for AI context | |
| $chipset = ''; | |
| $screen_size = ''; | |
| $display_type = ''; | |
| $refresh_rate = ''; | |
| $ram = ''; | |
| $storage = ''; | |
| $camera_rear = ''; | |
| $battery_capacity = ''; | |
| $os = ''; | |
| $os_version = ''; | |
| // Platform specs | |
| if (!empty($specs_data['platform'])) { | |
| $platform = $specs_data['platform']; | |
| $chipset = $platform['chipset'] ?? ''; | |
| $os = $platform['os'] ?? ''; | |
| $os_version = $platform['os_version'] ?? ''; | |
| } | |
| // Display specs | |
| if (!empty($specs_data['display'])) { | |
| $display = $specs_data['display']; | |
| $screen_size = $display['size'] ?? ''; | |
| $display_type = $display['type'] ?? ''; | |
| $refresh_rate = $display['refresh_rate'] ?? ''; | |
| } | |
| // Memory specs | |
| if (!empty($specs_data['memory'])) { | |
| $memory = $specs_data['memory']; | |
| $ram = $memory['ram'] ?? ''; | |
| $storage = $memory['storage'] ?? ''; | |
| } | |
| // Camera specs | |
| if (!empty($specs_data['main-camera'])) { | |
| $camera = $specs_data['main-camera']; | |
| $camera_rear = $camera['modules'] ?? ''; | |
| } | |
| // Battery specs | |
| if (!empty($specs_data['battery'])) { | |
| $battery = $specs_data['battery']; | |
| $battery_capacity = $battery['capacity'] ?? ''; | |
| } | |
| return array( | |
| // Basic Info | |
| 'title' => $device_data['name'] ?? '', | |
| 'description' => $device_data['description'] ?? '', | |
| 'release_date' => $release_date, | |
| 'release_year' => $release_year, | |
| 'manufacturer' => $manufacturer, | |
| 'device_type' => $device_type, | |
| 'category' => $category, | |
| 'status' => $status, | |
| // Key Specs (for AI context) | |
| 'chipset' => $chipset, | |
| 'screen_size' => $screen_size, | |
| 'display_type' => $display_type, | |
| 'refresh_rate' => $refresh_rate, | |
| 'ram' => $ram, | |
| 'storage' => $storage, | |
| 'camera_rear' => $camera_rear, | |
| 'battery_capacity' => $battery_capacity, | |
| 'os' => $os, | |
| 'os_version' => $os_version, | |
| ); | |
| } | |
| /** | |
| * Generate device description/excerpt from device data. | |
| * | |
| * Creates a short description similar to MobileAPI.dev search results format. | |
| * | |
| * @since 7.12.2 | |
| * @param array $device_data Device data from MobileAPI.dev. | |
| * @param array $specs_data Specs data from MobileAPI.dev. | |
| * @return string Generated description. | |
| */ | |
| private function generate_device_description($device_data, $specs_data) | |
| { | |
| $parts = array(); | |
| // Device name and type | |
| $device_name = $device_data['name'] ?? ''; | |
| $device_type = strtolower($device_data['device_type'] ?? 'phone'); | |
| $device_type_label = ucfirst($device_type); | |
| if (!empty($device_name)) { | |
| $parts[] = $device_name . ' ' . $device_type_label; | |
| } | |
| // Release date/announcement | |
| $release_date = $this->parse_release_date($device_data['release_date'] ?? ''); | |
| if (!empty($release_date)) { | |
| $release_timestamp = strtotime($release_date); | |
| $today = time(); | |
| if ($release_timestamp > $today) { | |
| // Upcoming device | |
| $date_formatted = date('M Y', $release_timestamp); | |
| $parts[] = 'Announced ' . $date_formatted; | |
| } else { | |
| // Released device | |
| $date_formatted = date('M Y', $release_timestamp); | |
| $parts[] = 'Released ' . $date_formatted; | |
| } | |
| } | |
| // Key features/specs | |
| $features = array(); | |
| // Display | |
| if (!empty($specs_data['display'])) { | |
| $display = $specs_data['display']; | |
| $display_parts = array(); | |
| if (!empty($display['size'])) { | |
| $display_parts[] = $display['size'] . '" display'; | |
| } | |
| if (!empty($display['type'])) { | |
| $display_parts[] = $display['type']; | |
| } | |
| if (!empty($display_parts)) { | |
| $features[] = implode(', ', $display_parts); | |
| } | |
| } | |
| // Chipset | |
| if (!empty($specs_data['platform']['chipset'])) { | |
| $features[] = $specs_data['platform']['chipset'] . ' chipset'; | |
| } | |
| // Battery | |
| if (!empty($specs_data['battery']['capacity'])) { | |
| $battery_cap = $specs_data['battery']['capacity']; | |
| // Extract number if it's like "3274 mAh" | |
| if (preg_match('/(\d+)/', $battery_cap, $matches)) { | |
| $features[] = $matches[1] . ' mAh battery'; | |
| } else { | |
| $features[] = $battery_cap . ' battery'; | |
| } | |
| } | |
| // Storage | |
| if (!empty($specs_data['memory']['storage'])) { | |
| $storage = $specs_data['memory']['storage']; | |
| // Extract number if it's like "128GB" or "128 GB" | |
| if (preg_match('/(\d+)\s*GB/i', $storage, $matches)) { | |
| $features[] = $matches[1] . ' GB storage'; | |
| } else { | |
| $features[] = $storage . ' storage'; | |
| } | |
| } | |
| // RAM | |
| if (!empty($specs_data['memory']['ram'])) { | |
| $ram = $specs_data['memory']['ram']; | |
| // Extract number if it's like "8GB" or "8 GB" | |
| if (preg_match('/(\d+)\s*GB/i', $ram, $matches)) { | |
| $features[] = $matches[1] . ' GB RAM'; | |
| } else { | |
| $features[] = $ram . ' RAM'; | |
| } | |
| } | |
| // Build description | |
| $description = ''; | |
| if (!empty($parts)) { | |
| $description = implode('. ', $parts) . '.'; | |
| } | |
| if (!empty($features)) { | |
| if (!empty($description)) { | |
| $description .= ' Features '; | |
| } else { | |
| $description = 'Features '; | |
| } | |
| $description .= implode(', ', array_slice($features, 0, 5)); // Limit to 5 key features | |
| $description .= '.'; | |
| } | |
| return trim($description); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment