reference plugin and image gen analysis
This commit is contained in:
436
igny8-ai-seo-wp-plugin/assets/js/image-queue-processor.js
Normal file
436
igny8-ai-seo-wp-plugin/assets/js/image-queue-processor.js
Normal file
@@ -0,0 +1,436 @@
|
||||
/**
|
||||
* Igny8 Image Queue Processor
|
||||
* Sequential image generation with individual progress tracking
|
||||
*/
|
||||
|
||||
// Process AI Image Generation for Drafts (Sequential Image Processing with Queue Modal)
|
||||
function processAIImageGenerationDrafts(postIds) {
|
||||
console.log('Igny8: processAIImageGenerationDrafts called with postIds:', postIds);
|
||||
|
||||
// Event 1: Generate Images button clicked
|
||||
if (window.addImageGenDebugLog) {
|
||||
window.addImageGenDebugLog('INFO', 'Generate Images button clicked', {
|
||||
postIds: postIds,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
|
||||
// Get image generation settings from saved options (passed via wp_localize_script)
|
||||
const desktopEnabled = window.IGNY8_PAGE?.imageSettings?.desktop_enabled || false;
|
||||
const mobileEnabled = window.IGNY8_PAGE?.imageSettings?.mobile_enabled || false;
|
||||
const maxInArticleImages = window.IGNY8_PAGE?.imageSettings?.max_in_article_images || 1;
|
||||
|
||||
// Event 2: Settings retrieved
|
||||
if (window.addImageGenDebugLog) {
|
||||
window.addImageGenDebugLog('SUCCESS', 'Settings retrieved', {
|
||||
desktop: desktopEnabled,
|
||||
mobile: mobileEnabled,
|
||||
maxImages: maxInArticleImages
|
||||
});
|
||||
}
|
||||
|
||||
// Build image queue based on settings
|
||||
const imageQueue = [];
|
||||
|
||||
postIds.forEach((postId, postIndex) => {
|
||||
// Featured image (always)
|
||||
imageQueue.push({
|
||||
post_id: postId,
|
||||
post_number: postIndex + 1,
|
||||
type: 'featured',
|
||||
device: '',
|
||||
label: 'Featured Image',
|
||||
post_title: `Post ${postIndex + 1}`
|
||||
});
|
||||
|
||||
// Desktop in-article images
|
||||
if (desktopEnabled) {
|
||||
for (let i = 1; i <= maxInArticleImages; i++) {
|
||||
imageQueue.push({
|
||||
post_id: postId,
|
||||
post_number: postIndex + 1,
|
||||
type: 'article',
|
||||
device: 'desktop',
|
||||
index: i,
|
||||
label: `desktop-${i}`,
|
||||
section: i,
|
||||
post_title: `Post ${postIndex + 1}`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Mobile in-article images
|
||||
if (mobileEnabled) {
|
||||
for (let i = 1; i <= maxInArticleImages; i++) {
|
||||
imageQueue.push({
|
||||
post_id: postId,
|
||||
post_number: postIndex + 1,
|
||||
type: 'article',
|
||||
device: 'mobile',
|
||||
index: i,
|
||||
label: `mobile-${i}`,
|
||||
section: i,
|
||||
post_title: `Post ${postIndex + 1}`
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Igny8: Image queue built:', imageQueue);
|
||||
|
||||
// Show queue modal
|
||||
showImageQueueModal(imageQueue, imageQueue.length);
|
||||
|
||||
// Start processing queue
|
||||
processImageQueue(imageQueue, 0);
|
||||
}
|
||||
|
||||
// Show modal with image queue and individual progress bars
|
||||
function showImageQueueModal(queue, totalImages) {
|
||||
if (window.currentProgressModal) {
|
||||
window.currentProgressModal.remove();
|
||||
}
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.id = 'igny8-image-queue-modal';
|
||||
modal.className = 'igny8-modal';
|
||||
|
||||
let queueHTML = '';
|
||||
queue.forEach((item, index) => {
|
||||
const itemId = `queue-item-${index}`;
|
||||
queueHTML += `
|
||||
<div class="queue-item" id="${itemId}" data-status="pending">
|
||||
<div style="display: flex; gap: 10px; align-items: center;">
|
||||
<div style="flex: 1;">
|
||||
<div class="queue-item-header">
|
||||
<span class="queue-number">${index + 1}</span>
|
||||
<span class="queue-label">${item.label}</span>
|
||||
<span class="queue-post-title">${item.post_title}</span>
|
||||
<span class="queue-status">⏳ Pending</span>
|
||||
</div>
|
||||
<div class="queue-progress-bar">
|
||||
<div class="queue-progress-fill" style="width: 0%"></div>
|
||||
<div class="queue-progress-text">0%</div>
|
||||
</div>
|
||||
<div class="queue-error" style="display: none;"></div>
|
||||
</div>
|
||||
<div class="queue-thumbnail" style="width: 75px; height: 75px; background: #f0f0f0; border-radius: 4px; display: flex; align-items: center; justify-content: center; overflow: hidden; flex-shrink: 0;">
|
||||
<span style="color: #999; font-size: 11px;">No image</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
modal.innerHTML = `
|
||||
<div class="igny8-modal-content" style="max-width: 950px; max-height: 80vh; overflow-y: auto;">
|
||||
<div class="igny8-modal-header">
|
||||
<h3>🎨 Generating Images</h3>
|
||||
<p style="margin: 5px 0; color: var(--text-light);">Total: ${totalImages} images in queue</p>
|
||||
</div>
|
||||
<div class="igny8-modal-body">
|
||||
<div class="image-queue-container">
|
||||
${queueHTML}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
.queue-item {
|
||||
margin-bottom: 12px;
|
||||
padding: 12px;
|
||||
background: var(--panel-2);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.queue-item[data-status="processing"] {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
border-color: rgb(59, 130, 246);
|
||||
}
|
||||
.queue-item[data-status="completed"] {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border-color: rgb(16, 185, 129);
|
||||
}
|
||||
.queue-item[data-status="failed"] {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
border-color: rgb(239, 68, 68);
|
||||
}
|
||||
.queue-item-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.queue-number {
|
||||
background: rgb(59, 130, 246);
|
||||
color: white;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.queue-label {
|
||||
font-weight: 600;
|
||||
}
|
||||
.queue-post-title {
|
||||
flex: 1;
|
||||
font-size: 12px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.queue-status {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.queue-progress-bar {
|
||||
height: 20px;
|
||||
background: rgba(0,0,0,0.1);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
.queue-progress-fill {
|
||||
height: 100%;
|
||||
background: rgb(59, 130, 246);
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
.queue-progress-text {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
text-shadow: 0 0 3px rgba(255,255,255,0.8);
|
||||
}
|
||||
.queue-item[data-status="completed"] .queue-progress-fill {
|
||||
background: rgb(16, 185, 129);
|
||||
}
|
||||
.queue-item[data-status="failed"] .queue-progress-fill {
|
||||
background: rgb(239, 68, 68);
|
||||
}
|
||||
.queue-error {
|
||||
margin-top: 8px;
|
||||
padding: 8px;
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
border-left: 3px solid rgb(239, 68, 68);
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
modal.classList.add('open');
|
||||
window.currentProgressModal = modal;
|
||||
}
|
||||
|
||||
// Process image queue sequentially with progressive loading
|
||||
function processImageQueue(queue, currentIndex) {
|
||||
if (currentIndex >= queue.length) {
|
||||
// All done
|
||||
console.log('Igny8: All images processed');
|
||||
|
||||
// Log to Image Generation Debug
|
||||
if (window.addImageGenDebugLog) {
|
||||
window.addImageGenDebugLog('SUCCESS', 'All images processed', {
|
||||
total: queue.length,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (window.currentProgressModal) {
|
||||
window.currentProgressModal.remove();
|
||||
window.currentProgressModal = null;
|
||||
}
|
||||
showNotification('Image generation complete!', 'success');
|
||||
|
||||
// Reload table
|
||||
if (window.loadTableData && window.IGNY8_PAGE?.tableId) {
|
||||
window.loadTableData(window.IGNY8_PAGE.tableId);
|
||||
}
|
||||
}, 2000);
|
||||
return;
|
||||
}
|
||||
|
||||
const item = queue[currentIndex];
|
||||
const itemElement = document.getElementById(`queue-item-${currentIndex}`);
|
||||
|
||||
if (!itemElement) {
|
||||
console.error('Queue item element not found:', currentIndex);
|
||||
|
||||
// Log to Image Generation Debug
|
||||
if (window.addImageGenDebugLog) {
|
||||
window.addImageGenDebugLog('ERROR', 'Queue item element not found', {
|
||||
index: currentIndex,
|
||||
itemId: `queue-item-${currentIndex}`
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(() => processImageQueue(queue, currentIndex + 1), 100);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update UI to processing
|
||||
itemElement.setAttribute('data-status', 'processing');
|
||||
itemElement.querySelector('.queue-status').textContent = '⏳ Generating...';
|
||||
|
||||
const progressFill = itemElement.querySelector('.queue-progress-fill');
|
||||
const progressText = itemElement.querySelector('.queue-progress-text');
|
||||
|
||||
// Log to Image Generation Debug
|
||||
if (window.addImageGenDebugLog) {
|
||||
window.addImageGenDebugLog('INFO', `Processing ${item.label}`, {
|
||||
postId: item.post_id,
|
||||
type: item.type,
|
||||
device: item.device || 'N/A',
|
||||
index: item.index || 1,
|
||||
queuePosition: `${currentIndex + 1}/${queue.length}`
|
||||
});
|
||||
}
|
||||
|
||||
// Progressive loading: 50% in 7s, 75% in next 5s, then 5% every second until 95%
|
||||
let currentProgress = 0;
|
||||
let phase = 1;
|
||||
let phaseStartTime = Date.now();
|
||||
|
||||
const progressInterval = setInterval(() => {
|
||||
const elapsed = Date.now() - phaseStartTime;
|
||||
|
||||
if (phase === 1 && currentProgress < 50) {
|
||||
// Phase 1: 0% to 50% in 7 seconds (7.14% per second)
|
||||
currentProgress += 0.714;
|
||||
if (currentProgress >= 50 || elapsed >= 7000) {
|
||||
currentProgress = 50;
|
||||
phase = 2;
|
||||
phaseStartTime = Date.now();
|
||||
}
|
||||
} else if (phase === 2 && currentProgress < 75) {
|
||||
// Phase 2: 50% to 75% in 5 seconds (5% per second)
|
||||
currentProgress += 0.5;
|
||||
if (currentProgress >= 75 || elapsed >= 5000) {
|
||||
currentProgress = 75;
|
||||
phase = 3;
|
||||
phaseStartTime = Date.now();
|
||||
}
|
||||
} else if (phase === 3 && currentProgress < 95) {
|
||||
// Phase 3: 75% to 95% - 5% every second
|
||||
if (elapsed >= 1000) {
|
||||
currentProgress = Math.min(95, currentProgress + 5);
|
||||
phaseStartTime = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
progressFill.style.width = currentProgress + '%';
|
||||
progressText.textContent = Math.round(currentProgress) + '%';
|
||||
}, 100);
|
||||
|
||||
// Generate single image
|
||||
const formData = new FormData();
|
||||
formData.append('action', 'igny8_ai_generate_single_image');
|
||||
formData.append('nonce', window.IGNY8_PAGE.nonce);
|
||||
formData.append('post_id', item.post_id);
|
||||
formData.append('type', item.type);
|
||||
formData.append('device', item.device || '');
|
||||
formData.append('index', item.index || 1);
|
||||
// Add meta box integration fields
|
||||
formData.append('image_label', item.label || '');
|
||||
formData.append('section', item.section || '');
|
||||
|
||||
fetch(window.IGNY8_PAGE.ajaxUrl, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// Stop progressive loading
|
||||
clearInterval(progressInterval);
|
||||
|
||||
if (data.success) {
|
||||
// Success - complete to 100%
|
||||
progressFill.style.width = '100%';
|
||||
progressText.textContent = '100%';
|
||||
itemElement.setAttribute('data-status', 'completed');
|
||||
itemElement.querySelector('.queue-status').textContent = '✅ Complete';
|
||||
|
||||
// Display thumbnail if image URL is available
|
||||
if (data.data?.image_url) {
|
||||
const thumbnailDiv = itemElement.querySelector('.queue-thumbnail');
|
||||
if (thumbnailDiv) {
|
||||
thumbnailDiv.innerHTML = `<img src="${data.data.image_url}" style="width: 100%; height: 100%; object-fit: cover; border-radius: 4px;" alt="Generated image">`;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✓ Image ${currentIndex + 1} generated successfully`);
|
||||
|
||||
// Log to Image Generation Debug
|
||||
if (window.addImageGenDebugLog) {
|
||||
window.addImageGenDebugLog('SUCCESS', `${item.label} generated successfully`, {
|
||||
postId: item.post_id,
|
||||
attachmentId: data.data?.attachment_id,
|
||||
provider: data.data?.provider,
|
||||
queuePosition: `${currentIndex + 1}/${queue.length}`
|
||||
});
|
||||
}
|
||||
|
||||
// Process next image after short delay
|
||||
setTimeout(() => processImageQueue(queue, currentIndex + 1), 500);
|
||||
} else {
|
||||
// Error - show at 90%
|
||||
progressFill.style.width = '90%';
|
||||
progressText.textContent = 'Failed';
|
||||
itemElement.setAttribute('data-status', 'failed');
|
||||
itemElement.querySelector('.queue-status').textContent = '❌ Failed';
|
||||
|
||||
const errorDiv = itemElement.querySelector('.queue-error');
|
||||
errorDiv.textContent = data.data?.message || 'Unknown error';
|
||||
errorDiv.style.display = 'block';
|
||||
|
||||
console.error(`✗ Image ${currentIndex + 1} failed:`, data.data?.message);
|
||||
|
||||
// Log to Image Generation Debug
|
||||
if (window.addImageGenDebugLog) {
|
||||
window.addImageGenDebugLog('ERROR', `${item.label} generation failed`, {
|
||||
postId: item.post_id,
|
||||
error: data.data?.message || 'Unknown error',
|
||||
queuePosition: `${currentIndex + 1}/${queue.length}`
|
||||
});
|
||||
}
|
||||
|
||||
// Continue to next image despite error
|
||||
setTimeout(() => processImageQueue(queue, currentIndex + 1), 1000);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// Exception - stop progressive loading
|
||||
clearInterval(progressInterval);
|
||||
|
||||
progressFill.style.width = '90%';
|
||||
progressText.textContent = 'Error';
|
||||
itemElement.setAttribute('data-status', 'failed');
|
||||
itemElement.querySelector('.queue-status').textContent = '❌ Error';
|
||||
|
||||
const errorDiv = itemElement.querySelector('.queue-error');
|
||||
errorDiv.textContent = 'Exception: ' + error.message;
|
||||
errorDiv.style.display = 'block';
|
||||
|
||||
console.error(`✗ Image ${currentIndex + 1} exception:`, error);
|
||||
|
||||
// Log to Image Generation Debug
|
||||
if (window.addImageGenDebugLog) {
|
||||
window.addImageGenDebugLog('ERROR', `${item.label} request exception`, {
|
||||
postId: item.post_id,
|
||||
error: error.message,
|
||||
queuePosition: `${currentIndex + 1}/${queue.length}`
|
||||
});
|
||||
}
|
||||
|
||||
// Continue to next image despite error
|
||||
setTimeout(() => processImageQueue(queue, currentIndex + 1), 1000);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user