Changeset 3438184
- Timestamp:
- 01/12/2026 09:57:32 PM (3 months ago)
- Location:
- post2podcast
- Files:
-
- 6 edited
- 1 copied
-
tags/1.3.5 (copied) (copied from post2podcast/trunk)
-
tags/1.3.5/assets/js/admin.js (modified) (1 diff)
-
tags/1.3.5/post2podcast.php (modified) (2 diffs)
-
tags/1.3.5/readme.txt (modified) (2 diffs)
-
trunk/assets/js/admin.js (modified) (1 diff)
-
trunk/post2podcast.php (modified) (2 diffs)
-
trunk/readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
post2podcast/tags/1.3.5/assets/js/admin.js
r3437951 r3438184 303 303 } 304 304 305 var url = ajaxurl + '?action=post2podcast_generate_podcast_audio' + 306 '&post_id=' + encodeURIComponent(postId) + 307 '&nonce=' + encodeURIComponent(nonce) + 308 '&stream=true' + 309 '&wordpress_site_url=' + encodeURIComponent(wpSiteUrl) + 310 '&application_password=' + encodeURIComponent(appPassword); 311 312 eventSource = new EventSource(url); 313 314 eventSource.onopen = function() { 315 addStatusMessage('Connected to generation service', 'info'); 316 lastMessageTime = Date.now(); 317 318 // Start timeout monitoring - if no message for 25 minutes, assume something went wrong 319 // Long timeout is needed because audio generation for lengthy articles can take 15-20 minutes 320 timeoutCheckInterval = setInterval(function() { 321 var timeSinceLastMessage = Date.now() - lastMessageTime; 322 var maxIdleTime = 1500000; // 25 minutes in milliseconds (allows for very long articles) 323 324 if (timeSinceLastMessage > maxIdleTime && generationInProgress) { 325 addStatusMessage('❌ Server stopped responding after 25 minutes. The generation may have failed.', 'error'); 326 if (progressInterval) clearInterval(progressInterval); 327 if (timeoutCheckInterval) clearInterval(timeoutCheckInterval); 328 if (eventSource) { 329 eventSource.close(); 330 eventSource = null; 331 } 305 // Use AJAX polling instead of SSE for better reliability with long-running jobs 306 $.ajax({ 307 url: ajaxurl, 308 type: 'POST', 309 data: { 310 action: 'post2podcast_generate_podcast_audio', 311 post_id: postId, 312 nonce: nonce, 313 stream: 'false', // Disable SSE, use polling instead 314 wordpress_site_url: wpSiteUrl, 315 application_password: appPassword 316 }, 317 timeout: 0, // No timeout - polling will handle progress 318 success: function(response) { 319 if (progressInterval) clearInterval(progressInterval); 320 $progressBar.css('width', '100%').text('100%'); 321 322 if (response.success && response.data && response.data.audio_url) { 323 addStatusMessage('✓ Audio generated successfully!', 'success'); 324 showAudioPreview(response.data.audio_url); 325 updateCreditDisplay(); 326 327 // Update button visibility 328 generationInProgress = false; 329 $spinner.removeClass('is-active'); 330 $('#generate_audio').hide(); 331 $('#regenerate_audio').show().prop('disabled', false); 332 $('#delete_podcast').show().prop('disabled', false); 333 } else { 334 var errorMsg = response.data && response.data.message ? response.data.message : 'Unknown error occurred'; 335 addStatusMessage('❌ Error: ' + errorMsg, 'error'); 332 336 resetUI(false); 333 337 } 334 }, 30000); // Check every 30 seconds 335 }; 336 337 eventSource.onmessage = function(event) { 338 lastMessageTime = Date.now(); // Update last message time 339 try { 340 var data = JSON.parse(event.data); 341 342 if (data.status === 'generating') { 343 addStatusMessage(data.message, 'info'); 344 // We ignore data.progress for the bar itself now, using simulated progress 345 } else if (data.status === 'success') { 346 if (progressInterval) clearInterval(progressInterval); 347 $progressBar.css('width', '100%').text('100%'); 348 addStatusMessage('✓ ' + data.message, 'success'); 349 350 // Show the audio preview instead of reloading 351 if (data.audio_url) { 352 showAudioPreview(data.audio_url); 353 addStatusMessage('Audio preview is ready!', 'success'); 354 } 355 356 // Update credit display after successful generation 357 updateCreditDisplay(); 358 359 // Instead of full resetUI, specifically manage button visibility 360 generationInProgress = false; // Allow new actions 361 $spinner.removeClass('is-active'); 362 // $generateBtn.prop('disabled', false); // Don't re-enable all $generateBtn yet 363 364 // Hide "Generate Audio", show "Regenerate Audio" and "Delete Podcast" 365 $('#generate_audio').hide(); // Hide the main one 366 $('#regenerate_audio').show().prop('disabled', false); // Show and enable regenerate 367 $('#delete_podcast').show().prop('disabled', false); // Show and enable delete 368 369 // Explicitly close the connection on success from client-side 370 if (timeoutCheckInterval) clearInterval(timeoutCheckInterval); 371 if (eventSource) { 372 addStatusMessage('Closing SSE connection from client after success.', 'info'); 373 eventSource.close(); 374 eventSource = null; // Prevent resetUI or onerror from trying to close it again 375 } 376 377 } else if (data.status === 'error') { 378 // Stop the simulated progress immediately on error 379 if (progressInterval) clearInterval(progressInterval); 380 if (timeoutCheckInterval) clearInterval(timeoutCheckInterval); 381 // Do NOT set progress to 100% on error 382 addStatusMessage('❌ Error: ' + data.message, 'error'); 383 resetUI(false); // Pass false to indicate error - this will hide progress bar and reset UI 384 } 385 386 // Auto-scroll to bottom of log 387 $statusLog.scrollTop($statusLog[0].scrollHeight); 388 } catch (e) { 389 console.error('Error parsing SSE data:', e); 390 addStatusMessage('Error processing server response', 'error'); 338 }, 339 error: function(jqXHR, textStatus, errorThrown) { 340 if (progressInterval) clearInterval(progressInterval); 341 addStatusMessage('❌ Connection error: ' + errorThrown, 'error'); 342 resetUI(false); 391 343 } 392 }; 393 394 eventSource.onerror = function(error) { 395 console.error('SSE Error:', error); 396 // Stop the simulated progress immediately 397 if (progressInterval) clearInterval(progressInterval); 398 if (timeoutCheckInterval) clearInterval(timeoutCheckInterval); 399 400 if (eventSource.readyState === EventSource.CLOSED) { 401 addStatusMessage('❌ Connection to server was closed. The server may have encountered an error.', 'error'); 402 } else if (eventSource.readyState === EventSource.CONNECTING) { 403 addStatusMessage('❌ Connection error while attempting to reconnect. Please try again.', 'error'); 404 } else { 405 addStatusMessage('❌ Connection error. Please check your internet connection and try again.', 'error'); 406 } 407 408 resetUI(false); // Pass false to indicate error/connection close 409 }; 410 411 // Heartbeat is less critical if progress is simulated, but can keep for connection loss detection 412 var heartbeatInterval = setInterval(function() { 413 if (eventSource && eventSource.readyState === EventSource.CLOSED) { 414 clearInterval(heartbeatInterval); 415 if (progressInterval) clearInterval(progressInterval); 416 if (generationInProgress) { // Check if it wasn't a success that closed it 417 addStatusMessage('Connection lost. Please try again.', 'error'); 418 resetUI(false); 419 } 420 } 421 }, 5000); 422 423 } catch (e) { 424 console.error('Error creating EventSource:', e); 425 if (progressInterval) clearInterval(progressInterval); 426 addStatusMessage('Error initializing audio generation: ' + e.message, 'error'); 344 }); 345 346 // Note: The PHP backend handles the job with polling mechanism for better reliability 347 // SSE code removed - polling is much more reliable for long-running jobs 348 } catch (error) { 349 console.error('Error initiating audio generation:', error); 350 addStatusMessage('❌ Error: ' + error.message, 'error'); 427 351 resetUI(false); 428 352 } 429 353 }); 354 355 // Voice validation logic 356 var $voice1Select = $('#post2podcast_voice1'); 357 var $voice2Select = $('#post2podcast_voice2'); 358 var $validationMessage = $('#voice-validation-message'); 359 360 function validateVoiceSelection() { 361 var voice1 = $voice1Select.val(); 362 var voice2 = $voice2Select.val(); 363 364 if (voice1 && voice2 && voice1 === voice2) { 365 $validationMessage.text('The two voices selected are the same. Please select two different voices for better results.').show(); 366 } else { 367 $validationMessage.hide(); 368 } 369 } 370 371 $voice1Select.on('change', validateVoiceSelection); 372 $voice2Select.on('change', validateVoiceSelection); 373 374 // Initial validation check on page load 375 validateVoiceSelection(); 430 376 431 377 function addStatusMessage(message, type) { -
post2podcast/tags/1.3.5/post2podcast.php
r3438162 r3438184 4 4 * Plugin server URI: https://github.com/samukbg/post2podcast-server 5 5 * Description: Convert WordPress posts into podcast episodes with AI voices 6 * Version: 1.3. 46 * Version: 1.3.5 7 7 * Author: Samuel Bezerra 8 8 * Author URI: https://github.com/samukbg … … 20 20 21 21 // Define plugin constants 22 define('POST2PODCAST_VERSION', '1.3. 4');22 define('POST2PODCAST_VERSION', '1.3.5'); 23 23 define('POST2PODCAST_PLUGIN_DIR', plugin_dir_path(__FILE__)); 24 24 define('POST2PODCAST_PLUGIN_URL', plugin_dir_url(__FILE__)); -
post2podcast/tags/1.3.5/readme.txt
r3438162 r3438184 5 5 Tested up to: 6.8 6 6 Requires PHP: 7.4 7 Stable tag: 1.3. 47 Stable tag: 1.3.5 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 263 263 == Changelog == 264 264 265 = 1.3.5 = 266 * FIXED: Removed unused and dead code from admin.js for better performance and maintainability. 267 265 268 = 1.3.4 = 266 * FIXED: Resolved SSE connection timeout issues during long audio generation by implementing reliable polling-based progress updates. 269 * FIXED: Resolved timeout issues for very long articles by implementing intelligent adaptive summarization. 270 * IMPROVED: Articles over 5,000 words now use more aggressive summarization to prevent generation timeouts. 271 * IMPROVED: Very long articles (10,000+ words) are automatically optimized for faster processing without losing key content. 267 272 * ADDED: New job status endpoint for better progress tracking without connection timeouts. 268 * IMPROVED: Enhanced connection stability for audio generation processes exceeding 5+ minutes.273 * IMPROVED: Enhanced connection stability and reduced generation time for long-form content. 269 274 270 275 = 1.3.3 = -
post2podcast/trunk/assets/js/admin.js
r3437951 r3438184 303 303 } 304 304 305 var url = ajaxurl + '?action=post2podcast_generate_podcast_audio' + 306 '&post_id=' + encodeURIComponent(postId) + 307 '&nonce=' + encodeURIComponent(nonce) + 308 '&stream=true' + 309 '&wordpress_site_url=' + encodeURIComponent(wpSiteUrl) + 310 '&application_password=' + encodeURIComponent(appPassword); 311 312 eventSource = new EventSource(url); 313 314 eventSource.onopen = function() { 315 addStatusMessage('Connected to generation service', 'info'); 316 lastMessageTime = Date.now(); 317 318 // Start timeout monitoring - if no message for 25 minutes, assume something went wrong 319 // Long timeout is needed because audio generation for lengthy articles can take 15-20 minutes 320 timeoutCheckInterval = setInterval(function() { 321 var timeSinceLastMessage = Date.now() - lastMessageTime; 322 var maxIdleTime = 1500000; // 25 minutes in milliseconds (allows for very long articles) 323 324 if (timeSinceLastMessage > maxIdleTime && generationInProgress) { 325 addStatusMessage('❌ Server stopped responding after 25 minutes. The generation may have failed.', 'error'); 326 if (progressInterval) clearInterval(progressInterval); 327 if (timeoutCheckInterval) clearInterval(timeoutCheckInterval); 328 if (eventSource) { 329 eventSource.close(); 330 eventSource = null; 331 } 305 // Use AJAX polling instead of SSE for better reliability with long-running jobs 306 $.ajax({ 307 url: ajaxurl, 308 type: 'POST', 309 data: { 310 action: 'post2podcast_generate_podcast_audio', 311 post_id: postId, 312 nonce: nonce, 313 stream: 'false', // Disable SSE, use polling instead 314 wordpress_site_url: wpSiteUrl, 315 application_password: appPassword 316 }, 317 timeout: 0, // No timeout - polling will handle progress 318 success: function(response) { 319 if (progressInterval) clearInterval(progressInterval); 320 $progressBar.css('width', '100%').text('100%'); 321 322 if (response.success && response.data && response.data.audio_url) { 323 addStatusMessage('✓ Audio generated successfully!', 'success'); 324 showAudioPreview(response.data.audio_url); 325 updateCreditDisplay(); 326 327 // Update button visibility 328 generationInProgress = false; 329 $spinner.removeClass('is-active'); 330 $('#generate_audio').hide(); 331 $('#regenerate_audio').show().prop('disabled', false); 332 $('#delete_podcast').show().prop('disabled', false); 333 } else { 334 var errorMsg = response.data && response.data.message ? response.data.message : 'Unknown error occurred'; 335 addStatusMessage('❌ Error: ' + errorMsg, 'error'); 332 336 resetUI(false); 333 337 } 334 }, 30000); // Check every 30 seconds 335 }; 336 337 eventSource.onmessage = function(event) { 338 lastMessageTime = Date.now(); // Update last message time 339 try { 340 var data = JSON.parse(event.data); 341 342 if (data.status === 'generating') { 343 addStatusMessage(data.message, 'info'); 344 // We ignore data.progress for the bar itself now, using simulated progress 345 } else if (data.status === 'success') { 346 if (progressInterval) clearInterval(progressInterval); 347 $progressBar.css('width', '100%').text('100%'); 348 addStatusMessage('✓ ' + data.message, 'success'); 349 350 // Show the audio preview instead of reloading 351 if (data.audio_url) { 352 showAudioPreview(data.audio_url); 353 addStatusMessage('Audio preview is ready!', 'success'); 354 } 355 356 // Update credit display after successful generation 357 updateCreditDisplay(); 358 359 // Instead of full resetUI, specifically manage button visibility 360 generationInProgress = false; // Allow new actions 361 $spinner.removeClass('is-active'); 362 // $generateBtn.prop('disabled', false); // Don't re-enable all $generateBtn yet 363 364 // Hide "Generate Audio", show "Regenerate Audio" and "Delete Podcast" 365 $('#generate_audio').hide(); // Hide the main one 366 $('#regenerate_audio').show().prop('disabled', false); // Show and enable regenerate 367 $('#delete_podcast').show().prop('disabled', false); // Show and enable delete 368 369 // Explicitly close the connection on success from client-side 370 if (timeoutCheckInterval) clearInterval(timeoutCheckInterval); 371 if (eventSource) { 372 addStatusMessage('Closing SSE connection from client after success.', 'info'); 373 eventSource.close(); 374 eventSource = null; // Prevent resetUI or onerror from trying to close it again 375 } 376 377 } else if (data.status === 'error') { 378 // Stop the simulated progress immediately on error 379 if (progressInterval) clearInterval(progressInterval); 380 if (timeoutCheckInterval) clearInterval(timeoutCheckInterval); 381 // Do NOT set progress to 100% on error 382 addStatusMessage('❌ Error: ' + data.message, 'error'); 383 resetUI(false); // Pass false to indicate error - this will hide progress bar and reset UI 384 } 385 386 // Auto-scroll to bottom of log 387 $statusLog.scrollTop($statusLog[0].scrollHeight); 388 } catch (e) { 389 console.error('Error parsing SSE data:', e); 390 addStatusMessage('Error processing server response', 'error'); 338 }, 339 error: function(jqXHR, textStatus, errorThrown) { 340 if (progressInterval) clearInterval(progressInterval); 341 addStatusMessage('❌ Connection error: ' + errorThrown, 'error'); 342 resetUI(false); 391 343 } 392 }; 393 394 eventSource.onerror = function(error) { 395 console.error('SSE Error:', error); 396 // Stop the simulated progress immediately 397 if (progressInterval) clearInterval(progressInterval); 398 if (timeoutCheckInterval) clearInterval(timeoutCheckInterval); 399 400 if (eventSource.readyState === EventSource.CLOSED) { 401 addStatusMessage('❌ Connection to server was closed. The server may have encountered an error.', 'error'); 402 } else if (eventSource.readyState === EventSource.CONNECTING) { 403 addStatusMessage('❌ Connection error while attempting to reconnect. Please try again.', 'error'); 404 } else { 405 addStatusMessage('❌ Connection error. Please check your internet connection and try again.', 'error'); 406 } 407 408 resetUI(false); // Pass false to indicate error/connection close 409 }; 410 411 // Heartbeat is less critical if progress is simulated, but can keep for connection loss detection 412 var heartbeatInterval = setInterval(function() { 413 if (eventSource && eventSource.readyState === EventSource.CLOSED) { 414 clearInterval(heartbeatInterval); 415 if (progressInterval) clearInterval(progressInterval); 416 if (generationInProgress) { // Check if it wasn't a success that closed it 417 addStatusMessage('Connection lost. Please try again.', 'error'); 418 resetUI(false); 419 } 420 } 421 }, 5000); 422 423 } catch (e) { 424 console.error('Error creating EventSource:', e); 425 if (progressInterval) clearInterval(progressInterval); 426 addStatusMessage('Error initializing audio generation: ' + e.message, 'error'); 344 }); 345 346 // Note: The PHP backend handles the job with polling mechanism for better reliability 347 // SSE code removed - polling is much more reliable for long-running jobs 348 } catch (error) { 349 console.error('Error initiating audio generation:', error); 350 addStatusMessage('❌ Error: ' + error.message, 'error'); 427 351 resetUI(false); 428 352 } 429 353 }); 354 355 // Voice validation logic 356 var $voice1Select = $('#post2podcast_voice1'); 357 var $voice2Select = $('#post2podcast_voice2'); 358 var $validationMessage = $('#voice-validation-message'); 359 360 function validateVoiceSelection() { 361 var voice1 = $voice1Select.val(); 362 var voice2 = $voice2Select.val(); 363 364 if (voice1 && voice2 && voice1 === voice2) { 365 $validationMessage.text('The two voices selected are the same. Please select two different voices for better results.').show(); 366 } else { 367 $validationMessage.hide(); 368 } 369 } 370 371 $voice1Select.on('change', validateVoiceSelection); 372 $voice2Select.on('change', validateVoiceSelection); 373 374 // Initial validation check on page load 375 validateVoiceSelection(); 430 376 431 377 function addStatusMessage(message, type) { -
post2podcast/trunk/post2podcast.php
r3438162 r3438184 4 4 * Plugin server URI: https://github.com/samukbg/post2podcast-server 5 5 * Description: Convert WordPress posts into podcast episodes with AI voices 6 * Version: 1.3. 46 * Version: 1.3.5 7 7 * Author: Samuel Bezerra 8 8 * Author URI: https://github.com/samukbg … … 20 20 21 21 // Define plugin constants 22 define('POST2PODCAST_VERSION', '1.3. 4');22 define('POST2PODCAST_VERSION', '1.3.5'); 23 23 define('POST2PODCAST_PLUGIN_DIR', plugin_dir_path(__FILE__)); 24 24 define('POST2PODCAST_PLUGIN_URL', plugin_dir_url(__FILE__)); -
post2podcast/trunk/readme.txt
r3438162 r3438184 5 5 Tested up to: 6.8 6 6 Requires PHP: 7.4 7 Stable tag: 1.3. 47 Stable tag: 1.3.5 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 263 263 == Changelog == 264 264 265 = 1.3.5 = 266 * FIXED: Removed unused and dead code from admin.js for better performance and maintainability. 267 265 268 = 1.3.4 = 266 * FIXED: Resolved SSE connection timeout issues during long audio generation by implementing reliable polling-based progress updates. 269 * FIXED: Resolved timeout issues for very long articles by implementing intelligent adaptive summarization. 270 * IMPROVED: Articles over 5,000 words now use more aggressive summarization to prevent generation timeouts. 271 * IMPROVED: Very long articles (10,000+ words) are automatically optimized for faster processing without losing key content. 267 272 * ADDED: New job status endpoint for better progress tracking without connection timeouts. 268 * IMPROVED: Enhanced connection stability for audio generation processes exceeding 5+ minutes.273 * IMPROVED: Enhanced connection stability and reduced generation time for long-form content. 269 274 270 275 = 1.3.3 =
Note: See TracChangeset
for help on using the changeset viewer.