'attachment', 'post_status' => 'inherit', 'posts_per_page' => (int) ($args['per_page'] ?? 20), 'paged' => (int) ($args['page'] ?? 1)]; if (!empty($args['mime_type'])) $qArgs['post_mime_type'] = $args['mime_type']; if (!empty($args['search'])) $qArgs['s'] = $args['search']; $query = new \WP_Query($qArgs); $items = []; foreach ($query->posts as $p) { $items[] = ['id' => $p->ID, 'title' => $p->post_title, 'url' => wp_get_attachment_url($p->ID), 'mime_type' => $p->post_mime_type, 'alt' => get_post_meta($p->ID, '_wp_attachment_image_alt', true), 'date' => $p->post_date]; } return ['media' => $items, 'total' => $query->found_posts]; } public static function getMedia(array $args): array { $id = (int) $args['media_id']; $post = get_post($id); if (!$post || $post->post_type !== 'attachment') throw new \RuntimeException("Media not found: {$id}"); $meta = wp_get_attachment_metadata($id); return ['id' => $id, 'media_id' => $id, 'title' => $post->post_title, 'url' => wp_get_attachment_url($id), 'mime_type' => $post->post_mime_type, 'alt' => get_post_meta($id, '_wp_attachment_image_alt', true), 'caption' => $post->post_excerpt, 'description' => $post->post_content, 'metadata' => $meta]; } public static function uploadMedia(array $args): array { require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/media.php'; require_once ABSPATH . 'wp-admin/includes/image.php'; $sourceUrl = $args['source_url']; $tmpFile = download_url($sourceUrl); if (is_wp_error($tmpFile)) throw new \RuntimeException($tmpFile->get_error_message()); // Derive a clean filename with proper extension $urlPath = parse_url($sourceUrl, PHP_URL_PATH); $fileName = basename($urlPath ?: 'upload'); // Detect MIME type from the downloaded file to avoid WP type-checking failures $mimeType = ''; if (function_exists('mime_content_type')) { $mimeType = mime_content_type($tmpFile) ?: ''; } if (empty($mimeType) && function_exists('finfo_open')) { $finfo = finfo_open(FILEINFO_MIME_TYPE); $mimeType = finfo_file($finfo, $tmpFile) ?: ''; finfo_close($finfo); } // Ensure the filename has the correct extension for the detected MIME type if ($mimeType) { $ext_map = [ 'image/jpeg' => 'jpg', 'image/jpg' => 'jpg', 'image/png' => 'png', 'image/gif' => 'gif', 'image/webp' => 'webp', 'image/svg+xml' => 'svg', 'image/avif' => 'avif', 'video/mp4' => 'mp4', 'video/webm' => 'webm', 'audio/mpeg' => 'mp3', 'audio/wav' => 'wav', 'application/pdf' => 'pdf', ]; $detectedExt = $ext_map[$mimeType] ?? null; if ($detectedExt) { $baseName = pathinfo($fileName, PATHINFO_FILENAME) ?: 'upload'; $fileName = $baseName . '.' . $detectedExt; } } $fileArray = [ 'name' => $fileName, 'tmp_name' => $tmpFile, 'type' => $mimeType ?: 'application/octet-stream', ]; $postId = (int) ($args['post_id'] ?? 0); // Temporarily allow all MIME types if WP blocks the detected type add_filter('upload_mimes', function($mimes) use ($mimeType, $fileArray) { if ($mimeType) { $ext = pathinfo($fileArray['name'], PATHINFO_EXTENSION); if ($ext) $mimes[$ext] = $mimeType; } return $mimes; }, 99); $mediaId = media_handle_sideload($fileArray, $postId); @unlink($tmpFile); if (is_wp_error($mediaId)) throw new \RuntimeException($mediaId->get_error_message()); if (!empty($args['title'])) wp_update_post(['ID' => $mediaId, 'post_title' => $args['title']]); if (!empty($args['alt_text'])) update_post_meta($mediaId, '_wp_attachment_image_alt', $args['alt_text']); if (!empty($args['caption'])) wp_update_post(['ID' => $mediaId, 'post_excerpt' => $args['caption']]); return ['media_id' => $mediaId, 'id' => $mediaId, 'url' => wp_get_attachment_url($mediaId)]; } public static function updateMedia(array $args): array { $id = (int) $args['media_id']; if (!$id) throw new \RuntimeException("media_id is required."); $post = get_post($id); if (!$post || $post->post_type !== 'attachment') throw new \RuntimeException("Media not found: {$id}"); $data = ['ID' => $id]; if (isset($args['title'])) $data['post_title'] = $args['title']; if (isset($args['caption'])) $data['post_excerpt'] = $args['caption']; if (isset($args['description'])) $data['post_content'] = $args['description']; if (count($data) > 1) wp_update_post($data); if (isset($args['alt_text'])) update_post_meta($id, '_wp_attachment_image_alt', $args['alt_text']); return ['media_id' => $id, 'id' => $id, 'updated' => true]; } public static function deleteMedia(array $args): array { $id = (int) $args['media_id']; if (!$id) throw new \RuntimeException("media_id is required."); $result = wp_delete_attachment($id, (bool) ($args['force'] ?? true)); if (!$result) throw new \RuntimeException("Failed to delete media: {$id}"); return ['media_id' => $id, 'id' => $id, 'deleted' => true]; } public static function countMedia(array $args): array { global $wpdb; $rows = $wpdb->get_results("SELECT post_mime_type, COUNT(*) as cnt FROM {$wpdb->posts} WHERE post_type='attachment' AND post_status='inherit' GROUP BY post_mime_type", ARRAY_A); $counts = ['total' => 0, 'image' => 0, 'video' => 0, 'audio' => 0, 'document' => 0, 'other' => 0]; foreach ($rows as $r) { $counts['total'] += (int) $r['cnt']; $type = explode('/', $r['post_mime_type'])[0]; if (isset($counts[$type])) $counts[$type] += (int) $r['cnt']; else $counts['other'] += (int) $r['cnt']; } return $counts; } }