// WordPress Media Service
import { AxiosInstance } from 'axios';
import FormData from 'form-data';
import * as fs from 'fs';
import * as path from 'path';
import { logger } from '../../utils/logger.js';
import { ErrorHandler, ErrorCategory, MCPError } from '../../utils/error-handler.js';
export interface MediaQueryParams {
per_page?: number;
page?: number;
search?: string;
media_type?: 'image' | 'video' | 'audio' | 'application';
mime_type?: string;
parent?: number;
}
export interface MediaUploadOptions {
title?: string;
alt_text?: string;
caption?: string;
description?: string;
post?: number;
}
export class MediaService {
constructor(private client: AxiosInstance, private baseUrl: string) {}
async getMedia(params: MediaQueryParams = {}): Promise<any> {
return ErrorHandler.wrapAsync(async () => {
const defaultParams = {
per_page: 10,
...params
};
logger.debug('Listing media', defaultParams);
const response = await this.client.get('/media', {
params: defaultParams
});
logger.debug(`Found ${response.data.length} media items`);
return {
media: response.data,
total: response.headers['x-wp-total'],
totalPages: response.headers['x-wp-totalpages']
};
}, 'MediaService.getMedia');
}
async getMediaItem(id: number): Promise<any> {
return ErrorHandler.wrapAsync(async () => {
logger.debug(`Fetching media item ${id}`);
const response = await this.client.get(`/media/${id}`);
return response.data;
}, 'MediaService.getMediaItem');
}
async uploadMedia(filePath: string, options: MediaUploadOptions = {}): Promise<any> {
return ErrorHandler.wrapAsync(async () => {
// Validate file exists
if (!fs.existsSync(filePath)) {
throw new MCPError(
`File not found: ${filePath}`,
ErrorCategory.FILE_OPERATION,
'FILE_NOT_FOUND'
);
}
const fileName = path.basename(filePath);
const fileStream = fs.createReadStream(filePath);
const fileStats = fs.statSync(filePath);
logger.info(`Uploading media file: ${fileName}`, {
size: fileStats.size,
path: filePath
});
// Create form data
const formData = new FormData();
formData.append('file', fileStream, fileName);
if (options.title) formData.append('title', options.title);
if (options.alt_text) formData.append('alt_text', options.alt_text);
if (options.caption) formData.append('caption', options.caption);
if (options.description) formData.append('description', options.description);
if (options.post) formData.append('post', options.post.toString());
// Upload to WordPress
const response = await this.client.post('/media', formData, {
headers: {
...formData.getHeaders(),
},
maxBodyLength: Infinity,
maxContentLength: Infinity,
});
logger.info('Media uploaded successfully', {
id: response.data.id,
url: response.data.source_url
});
return response.data;
}, 'MediaService.uploadMedia');
}
async updateMedia(id: number, data: Partial<MediaUploadOptions>): Promise<any> {
return ErrorHandler.wrapAsync(async () => {
logger.info(`Updating media item ${id}`);
const response = await this.client.post(`/media/${id}`, data);
logger.info('Media updated successfully', { id });
return response.data;
}, 'MediaService.updateMedia');
}
async deleteMedia(id: number, force: boolean = false): Promise<any> {
return ErrorHandler.wrapAsync(async () => {
logger.info(`Deleting media item ${id}`, { force });
const response = await this.client.delete(`/media/${id}`, {
params: { force }
});
logger.info('Media deleted successfully', { id });
return response.data;
}, 'MediaService.deleteMedia');
}
async getMediaByPost(postId: number): Promise<any> {
return this.getMedia({ parent: postId });
}
async searchMedia(searchTerm: string, params: Omit<MediaQueryParams, 'search'> = {}): Promise<any> {
return this.getMedia({ ...params, search: searchTerm });
}
}