Skip to content

Instantly share code, notes, and snippets.

@iamkingsleyf
Last active January 13, 2026 12:29
Show Gist options
  • Select an option

  • Save iamkingsleyf/3fd89285ee732ea7e70543f12f5fdc3b to your computer and use it in GitHub Desktop.

Select an option

Save iamkingsleyf/3fd89285ee732ea7e70543f12f5fdc3b to your computer and use it in GitHub Desktop.
<?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) . '/');
}
}
<?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