Plugin Directory

Changeset 3437893


Ignore:
Timestamp:
01/12/2026 03:45:01 PM (3 months ago)
Author:
samukbg
Message:

Tagging version 1.2.7

Location:
post2podcast
Files:
12 edited
2 copied

Legend:

Unmodified
Added
Removed
  • post2podcast/tags/1.2.6/trunk/assets/js/admin.js

    r3331992 r3437893  
    1414    var generationInProgress = false;
    1515    var progressInterval = null; // For simulated progress
     16    var lastMessageTime = null; // Track when we last received a message
     17    var timeoutCheckInterval = null; // For checking message timeout
    1618   
    1719    // Handle delete podcast button click (for existing audio)
     
    3941            eventSource = null;
    4042        }
    41        
     43
    4244        if (progressInterval) {
    4345            clearInterval(progressInterval);
    4446            progressInterval = null;
    4547        }
    46        
     48
     49        if (timeoutCheckInterval) {
     50            clearInterval(timeoutCheckInterval);
     51            timeoutCheckInterval = null;
     52        }
     53
    4754        generationInProgress = false;
    4855       
     
    144151                if (response.success) {
    145152                    var detection = response.data.detection_result;
    146                    
     153
    147154                    if (detection.language_code) {
    148155                        // Update the UI with detected language
    149                         var languageInfo = '<p><strong>Detected Language:</strong> ' + 
    150                                          (detection.language_name || detection.language_code) + 
     156                        var languageInfo = '<p><strong>Detected Language:</strong> ' +
     157                                         (detection.language_name || detection.language_code) +
    151158                                         ' <span class="language-code">(' + detection.language_code + ')</span></p>';
    152                         languageInfo += '<p class="description">Voices will be automatically selected based on detected language.</p>';
    153                        
     159
    154160                        $('#detected-language-info').html(languageInfo);
    155                        
    156                         // Show suggested voices if available
     161
     162                        // Automatically select suggested voices if available
    157163                        if (detection.suggested_voices) {
    158164                            console.log('Suggested voices for ' + detection.language_code + ':', detection.suggested_voices);
     165
     166                            var voice1 = detection.suggested_voices.voice1;
     167                            var voice2 = detection.suggested_voices.voice2;
     168
     169                            // Set the voice selectors to the suggested voices
     170                            var $voice1Select = $('#post2podcast_voice1');
     171                            var $voice2Select = $('#post2podcast_voice2');
     172
     173                            // Check if the voices exist in the dropdown and select them
     174                            if ($voice1Select.find('option[value="' + voice1 + '"]').length > 0) {
     175                                $voice1Select.val(voice1);
     176                                console.log('Selected voice1: ' + voice1);
     177                            } else {
     178                                console.warn('Voice ' + voice1 + ' not found in dropdown');
     179                            }
     180
     181                            if ($voice2Select.find('option[value="' + voice2 + '"]').length > 0) {
     182                                $voice2Select.val(voice2);
     183                                console.log('Selected voice2: ' + voice2);
     184                            } else {
     185                                console.warn('Voice ' + voice2 + ' not found in dropdown');
     186                            }
     187
     188                            // Update the language info to show selected voices
     189                            languageInfo += '<p class="description" style="color: #28a745; font-weight: 500;">✓ Voices automatically selected: ' +
     190                                          voice1 + ' and ' + voice2 + '</p>';
     191                            $('#detected-language-info').html(languageInfo);
     192                        } else {
     193                            languageInfo += '<p class="description">Please select appropriate voices for this language.</p>';
     194                            $('#detected-language-info').html(languageInfo);
    159195                        }
    160196                    } else {
     
    230266            eventSource.close();
    231267        }
    232        
    233         // Create a timestamp for this generation session
    234         var sessionId = 'session-' + Date.now();
    235        
     268
     269        // Clear any existing timeout check
     270        if (timeoutCheckInterval) {
     271            clearInterval(timeoutCheckInterval);
     272        }
     273
    236274        // Start Server-Sent Events connection
    237275        try {
     
    256294           
    257295            eventSource = new EventSource(url);
    258            
     296
    259297            eventSource.onopen = function() {
    260298                addStatusMessage('Connected to generation service', 'info');
     299                lastMessageTime = Date.now();
     300
     301                // Start timeout monitoring - if no message for 3 minutes, assume something went wrong
     302                timeoutCheckInterval = setInterval(function() {
     303                    var timeSinceLastMessage = Date.now() - lastMessageTime;
     304                    var maxIdleTime = 180000; // 3 minutes in milliseconds
     305
     306                    if (timeSinceLastMessage > maxIdleTime && generationInProgress) {
     307                        addStatusMessage('❌ Server stopped responding. The generation may have failed.', 'error');
     308                        if (progressInterval) clearInterval(progressInterval);
     309                        if (timeoutCheckInterval) clearInterval(timeoutCheckInterval);
     310                        if (eventSource) {
     311                            eventSource.close();
     312                            eventSource = null;
     313                        }
     314                        resetUI(false);
     315                    }
     316                }, 10000); // Check every 10 seconds
    261317            };
    262            
     318
    263319            eventSource.onmessage = function(event) {
     320                lastMessageTime = Date.now(); // Update last message time
    264321                try {
    265322                    var data = JSON.parse(event.data);
     
    272329                        $progressBar.css('width', '100%').text('100%');
    273330                        addStatusMessage('✓ ' + data.message, 'success');
    274                        
     331
    275332                        // Show the audio preview instead of reloading
    276333                        if (data.audio_url) {
     
    278335                            addStatusMessage('Audio preview is ready!', 'success');
    279336                        }
    280                        
     337
    281338                        // Update credit display after successful generation
    282339                        updateCreditDisplay();
    283                        
     340
    284341                        // Instead of full resetUI, specifically manage button visibility
    285342                        generationInProgress = false; // Allow new actions
     
    293350
    294351                        // Explicitly close the connection on success from client-side
     352                        if (timeoutCheckInterval) clearInterval(timeoutCheckInterval);
    295353                        if (eventSource) {
    296354                            addStatusMessage('Closing SSE connection from client after success.', 'info');
     
    298356                            eventSource = null; // Prevent resetUI or onerror from trying to close it again
    299357                        }
    300                        
     358
    301359                    } else if (data.status === 'error') {
     360                        // Stop the simulated progress immediately on error
     361                        if (progressInterval) clearInterval(progressInterval);
     362                        if (timeoutCheckInterval) clearInterval(timeoutCheckInterval);
     363                        // Do NOT set progress to 100% on error
    302364                        addStatusMessage('❌ Error: ' + data.message, 'error');
    303                         resetUI(true); // Pass true to indicate success, so progress bar stays at 100% briefly
     365                        resetUI(false); // Pass false to indicate error - this will hide progress bar and reset UI
    304366                    }
    305367                   
     
    314376            eventSource.onerror = function(error) {
    315377                console.error('SSE Error:', error);
     378                // Stop the simulated progress immediately
     379                if (progressInterval) clearInterval(progressInterval);
     380                if (timeoutCheckInterval) clearInterval(timeoutCheckInterval);
     381
    316382                if (eventSource.readyState === EventSource.CLOSED) {
    317                     addStatusMessage('Connection to server was closed', 'error');
     383                    addStatusMessage('❌ Connection to server was closed. The server may have encountered an error.', 'error');
     384                } else if (eventSource.readyState === EventSource.CONNECTING) {
     385                    addStatusMessage('❌ Connection error while attempting to reconnect. Please try again.', 'error');
    318386                } else {
    319                     addStatusMessage('Connection error. Please try again.', 'error');
     387                    addStatusMessage('❌ Connection error. Please check your internet connection and try again.', 'error');
    320388                }
    321                 if (progressInterval) clearInterval(progressInterval);
     389
    322390                resetUI(false); // Pass false to indicate error/connection close
    323391            };
     
    366434    }
    367435   
    368     function resetUI(isSuccess) { 
     436    function resetUI(isSuccess) {
    369437        generationInProgress = false;
    370438        $spinner.removeClass('is-active');
    371         if (progressInterval) clearInterval(progressInterval);
     439        if (progressInterval) {
     440            clearInterval(progressInterval);
     441            progressInterval = null;
     442        }
     443        if (timeoutCheckInterval) {
     444            clearInterval(timeoutCheckInterval);
     445            timeoutCheckInterval = null;
     446        }
    372447
    373448        if (!isSuccess) { // Only hide progress bar immediately if not a success case
     
    377452        // On success, the bar is at 100%. It will be hidden/reset if user navigates or starts new generation.
    378453
    379         var audioExists = ($('#preview-audio-source').attr('src') && $('#preview-audio-source').attr('src') !== '') || 
     454        var audioExists = ($('#preview-audio-source').attr('src') && $('#preview-audio-source').attr('src') !== '') ||
    380455                          ($('.post2podcast-player audio source').length > 0 && $('.post2podcast-player audio source').attr('src') !== '');
    381456
     
    389464             $('#delete_podcast').hide();
    390465        }
    391        
     466
    392467        if (eventSource) {
    393468            eventSource.close();
     
    458533            eventSource.close();
    459534        }
     535        if (timeoutCheckInterval) {
     536            clearInterval(timeoutCheckInterval);
     537        }
    460538    });
    461539   
  • post2podcast/tags/1.2.6/trunk/includes/class-post2podcast-api.php

    r3437853 r3437893  
    328328                }
    329329                if (!isset($job_response['audio_content'])) {
    330                     throw new Exception('Audio content not found in API response.');
     330                    $error_detail = isset($job_response['detail']) ? $job_response['detail'] : 'Unknown error';
     331                    throw new Exception('Audio generation failed on server: ' . $error_detail);
    331332                }
    332333                $audio_content = base64_decode($job_response['audio_content']);
     
    682683            $error_message = $response->get_error_message();
    683684            error_log("Post2Podcast WP HTTP API Error (post2podcast_start_sync_generation): " . $error_message);
    684             throw new Exception('API request failed (WP HTTP API error): ' . esc_html($error_message));
     685            return new WP_Error('connection_error', 'Connection error: ' . $error_message);
    685686        }
    686687
     
    690691        if ($status_code >= 400) {
    691692            error_log("Post2Podcast API Error (post2podcast_start_sync_generation): Status " . $status_code . " - Response: " . $response_body);
    692             throw new Exception('API request failed with status code ' . esc_html($status_code) . ': ' . esc_html($response_body));
    693         }
    694         return json_decode($response_body, true);
     693
     694            // Try to parse error details from response
     695            $error_data = json_decode($response_body, true);
     696            $error_message = isset($error_data['detail']) ? $error_data['detail'] : $response_body;
     697
     698            return new WP_Error('api_error', 'Server error (' . $status_code . '): ' . $error_message);
     699        }
     700
     701        $decoded = json_decode($response_body, true);
     702
     703        // Check if the response indicates a server-side error even with 200 status
     704        if (!$decoded || (isset($decoded['error']) && $decoded['error'])) {
     705            $error_msg = isset($decoded['error']) ? $decoded['error'] : 'Invalid response from server';
     706            error_log("Post2Podcast API Error: " . $error_msg);
     707            return new WP_Error('generation_failed', $error_msg);
     708        }
     709
     710        return $decoded;
    695711    }
    696712
  • post2podcast/tags/1.2.6/trunk/post2podcast.php

    r3437853 r3437893  
    44 * Plugin server URI: https://github.com/samukbg/post2podcast-server
    55 * Description: Convert WordPress posts into podcast episodes with AI voices
    6  * Version: 1.2.6
     6 * Version: 1.2.7
    77 * Author: Samuel Bezerra
    88 * Author URI: https://github.com/samukbg
     
    2020
    2121// Define plugin constants
    22 define('POST2PODCAST_VERSION', '1.2.6');
     22define('POST2PODCAST_VERSION', '1.2.7');
    2323define('POST2PODCAST_PLUGIN_DIR', plugin_dir_path(__FILE__));
    2424define('POST2PODCAST_PLUGIN_URL', plugin_dir_url(__FILE__));
  • post2podcast/tags/1.2.6/trunk/readme.txt

    r3437853 r3437893  
    55Tested up to: 6.8
    66Requires PHP: 7.4
    7 Stable tag: 1.2.6
     7Stable tag: 1.2.7
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    274274== Changelog ==
    275275
     276= 1.2.7 =
     277* FIXED: Error handling now properly detects and reports server failures instead of showing 100% progress
     278* FIXED: Language detection now automatically selects appropriate Kokoro TTS voices in dropdown menus
     279* IMPROVED: Automatic server fallback - when one TTS server fails, automatically tries the next available server
     280* IMPROVED: Added 3-minute timeout detection to catch when server stops responding during generation
     281* IMPROVED: Better error messages showing specific failure reasons (connection errors, server errors, timeouts)
     282* IMPROVED: Progress bar now correctly stops on errors instead of completing to 100%
     283* ENHANCED: Backend now uses Kokoro TTS voice codes for all language-specific voice recommendations
     284
    276285= 1.2.6 =
    277286* IMPROVED: Minor bug fixes and performance improvements.
  • post2podcast/tags/1.2.7/assets/js/admin.js

    r3331992 r3437893  
    1414    var generationInProgress = false;
    1515    var progressInterval = null; // For simulated progress
     16    var lastMessageTime = null; // Track when we last received a message
     17    var timeoutCheckInterval = null; // For checking message timeout
    1618   
    1719    // Handle delete podcast button click (for existing audio)
     
    3941            eventSource = null;
    4042        }
    41        
     43
    4244        if (progressInterval) {
    4345            clearInterval(progressInterval);
    4446            progressInterval = null;
    4547        }
    46        
     48
     49        if (timeoutCheckInterval) {
     50            clearInterval(timeoutCheckInterval);
     51            timeoutCheckInterval = null;
     52        }
     53
    4754        generationInProgress = false;
    4855       
     
    144151                if (response.success) {
    145152                    var detection = response.data.detection_result;
    146                    
     153
    147154                    if (detection.language_code) {
    148155                        // Update the UI with detected language
    149                         var languageInfo = '<p><strong>Detected Language:</strong> ' + 
    150                                          (detection.language_name || detection.language_code) + 
     156                        var languageInfo = '<p><strong>Detected Language:</strong> ' +
     157                                         (detection.language_name || detection.language_code) +
    151158                                         ' <span class="language-code">(' + detection.language_code + ')</span></p>';
    152                         languageInfo += '<p class="description">Voices will be automatically selected based on detected language.</p>';
    153                        
     159
    154160                        $('#detected-language-info').html(languageInfo);
    155                        
    156                         // Show suggested voices if available
     161
     162                        // Automatically select suggested voices if available
    157163                        if (detection.suggested_voices) {
    158164                            console.log('Suggested voices for ' + detection.language_code + ':', detection.suggested_voices);
     165
     166                            var voice1 = detection.suggested_voices.voice1;
     167                            var voice2 = detection.suggested_voices.voice2;
     168
     169                            // Set the voice selectors to the suggested voices
     170                            var $voice1Select = $('#post2podcast_voice1');
     171                            var $voice2Select = $('#post2podcast_voice2');
     172
     173                            // Check if the voices exist in the dropdown and select them
     174                            if ($voice1Select.find('option[value="' + voice1 + '"]').length > 0) {
     175                                $voice1Select.val(voice1);
     176                                console.log('Selected voice1: ' + voice1);
     177                            } else {
     178                                console.warn('Voice ' + voice1 + ' not found in dropdown');
     179                            }
     180
     181                            if ($voice2Select.find('option[value="' + voice2 + '"]').length > 0) {
     182                                $voice2Select.val(voice2);
     183                                console.log('Selected voice2: ' + voice2);
     184                            } else {
     185                                console.warn('Voice ' + voice2 + ' not found in dropdown');
     186                            }
     187
     188                            // Update the language info to show selected voices
     189                            languageInfo += '<p class="description" style="color: #28a745; font-weight: 500;">✓ Voices automatically selected: ' +
     190                                          voice1 + ' and ' + voice2 + '</p>';
     191                            $('#detected-language-info').html(languageInfo);
     192                        } else {
     193                            languageInfo += '<p class="description">Please select appropriate voices for this language.</p>';
     194                            $('#detected-language-info').html(languageInfo);
    159195                        }
    160196                    } else {
     
    230266            eventSource.close();
    231267        }
    232        
    233         // Create a timestamp for this generation session
    234         var sessionId = 'session-' + Date.now();
    235        
     268
     269        // Clear any existing timeout check
     270        if (timeoutCheckInterval) {
     271            clearInterval(timeoutCheckInterval);
     272        }
     273
    236274        // Start Server-Sent Events connection
    237275        try {
     
    256294           
    257295            eventSource = new EventSource(url);
    258            
     296
    259297            eventSource.onopen = function() {
    260298                addStatusMessage('Connected to generation service', 'info');
     299                lastMessageTime = Date.now();
     300
     301                // Start timeout monitoring - if no message for 3 minutes, assume something went wrong
     302                timeoutCheckInterval = setInterval(function() {
     303                    var timeSinceLastMessage = Date.now() - lastMessageTime;
     304                    var maxIdleTime = 180000; // 3 minutes in milliseconds
     305
     306                    if (timeSinceLastMessage > maxIdleTime && generationInProgress) {
     307                        addStatusMessage('❌ Server stopped responding. The generation may have failed.', 'error');
     308                        if (progressInterval) clearInterval(progressInterval);
     309                        if (timeoutCheckInterval) clearInterval(timeoutCheckInterval);
     310                        if (eventSource) {
     311                            eventSource.close();
     312                            eventSource = null;
     313                        }
     314                        resetUI(false);
     315                    }
     316                }, 10000); // Check every 10 seconds
    261317            };
    262            
     318
    263319            eventSource.onmessage = function(event) {
     320                lastMessageTime = Date.now(); // Update last message time
    264321                try {
    265322                    var data = JSON.parse(event.data);
     
    272329                        $progressBar.css('width', '100%').text('100%');
    273330                        addStatusMessage('✓ ' + data.message, 'success');
    274                        
     331
    275332                        // Show the audio preview instead of reloading
    276333                        if (data.audio_url) {
     
    278335                            addStatusMessage('Audio preview is ready!', 'success');
    279336                        }
    280                        
     337
    281338                        // Update credit display after successful generation
    282339                        updateCreditDisplay();
    283                        
     340
    284341                        // Instead of full resetUI, specifically manage button visibility
    285342                        generationInProgress = false; // Allow new actions
     
    293350
    294351                        // Explicitly close the connection on success from client-side
     352                        if (timeoutCheckInterval) clearInterval(timeoutCheckInterval);
    295353                        if (eventSource) {
    296354                            addStatusMessage('Closing SSE connection from client after success.', 'info');
     
    298356                            eventSource = null; // Prevent resetUI or onerror from trying to close it again
    299357                        }
    300                        
     358
    301359                    } else if (data.status === 'error') {
     360                        // Stop the simulated progress immediately on error
     361                        if (progressInterval) clearInterval(progressInterval);
     362                        if (timeoutCheckInterval) clearInterval(timeoutCheckInterval);
     363                        // Do NOT set progress to 100% on error
    302364                        addStatusMessage('❌ Error: ' + data.message, 'error');
    303                         resetUI(true); // Pass true to indicate success, so progress bar stays at 100% briefly
     365                        resetUI(false); // Pass false to indicate error - this will hide progress bar and reset UI
    304366                    }
    305367                   
     
    314376            eventSource.onerror = function(error) {
    315377                console.error('SSE Error:', error);
     378                // Stop the simulated progress immediately
     379                if (progressInterval) clearInterval(progressInterval);
     380                if (timeoutCheckInterval) clearInterval(timeoutCheckInterval);
     381
    316382                if (eventSource.readyState === EventSource.CLOSED) {
    317                     addStatusMessage('Connection to server was closed', 'error');
     383                    addStatusMessage('❌ Connection to server was closed. The server may have encountered an error.', 'error');
     384                } else if (eventSource.readyState === EventSource.CONNECTING) {
     385                    addStatusMessage('❌ Connection error while attempting to reconnect. Please try again.', 'error');
    318386                } else {
    319                     addStatusMessage('Connection error. Please try again.', 'error');
     387                    addStatusMessage('❌ Connection error. Please check your internet connection and try again.', 'error');
    320388                }
    321                 if (progressInterval) clearInterval(progressInterval);
     389
    322390                resetUI(false); // Pass false to indicate error/connection close
    323391            };
     
    366434    }
    367435   
    368     function resetUI(isSuccess) { 
     436    function resetUI(isSuccess) {
    369437        generationInProgress = false;
    370438        $spinner.removeClass('is-active');
    371         if (progressInterval) clearInterval(progressInterval);
     439        if (progressInterval) {
     440            clearInterval(progressInterval);
     441            progressInterval = null;
     442        }
     443        if (timeoutCheckInterval) {
     444            clearInterval(timeoutCheckInterval);
     445            timeoutCheckInterval = null;
     446        }
    372447
    373448        if (!isSuccess) { // Only hide progress bar immediately if not a success case
     
    377452        // On success, the bar is at 100%. It will be hidden/reset if user navigates or starts new generation.
    378453
    379         var audioExists = ($('#preview-audio-source').attr('src') && $('#preview-audio-source').attr('src') !== '') || 
     454        var audioExists = ($('#preview-audio-source').attr('src') && $('#preview-audio-source').attr('src') !== '') ||
    380455                          ($('.post2podcast-player audio source').length > 0 && $('.post2podcast-player audio source').attr('src') !== '');
    381456
     
    389464             $('#delete_podcast').hide();
    390465        }
    391        
     466
    392467        if (eventSource) {
    393468            eventSource.close();
     
    458533            eventSource.close();
    459534        }
     535        if (timeoutCheckInterval) {
     536            clearInterval(timeoutCheckInterval);
     537        }
    460538    });
    461539   
  • post2podcast/tags/1.2.7/includes/class-post2podcast-api.php

    r3437853 r3437893  
    328328                }
    329329                if (!isset($job_response['audio_content'])) {
    330                     throw new Exception('Audio content not found in API response.');
     330                    $error_detail = isset($job_response['detail']) ? $job_response['detail'] : 'Unknown error';
     331                    throw new Exception('Audio generation failed on server: ' . $error_detail);
    331332                }
    332333                $audio_content = base64_decode($job_response['audio_content']);
     
    682683            $error_message = $response->get_error_message();
    683684            error_log("Post2Podcast WP HTTP API Error (post2podcast_start_sync_generation): " . $error_message);
    684             throw new Exception('API request failed (WP HTTP API error): ' . esc_html($error_message));
     685            return new WP_Error('connection_error', 'Connection error: ' . $error_message);
    685686        }
    686687
     
    690691        if ($status_code >= 400) {
    691692            error_log("Post2Podcast API Error (post2podcast_start_sync_generation): Status " . $status_code . " - Response: " . $response_body);
    692             throw new Exception('API request failed with status code ' . esc_html($status_code) . ': ' . esc_html($response_body));
    693         }
    694         return json_decode($response_body, true);
     693
     694            // Try to parse error details from response
     695            $error_data = json_decode($response_body, true);
     696            $error_message = isset($error_data['detail']) ? $error_data['detail'] : $response_body;
     697
     698            return new WP_Error('api_error', 'Server error (' . $status_code . '): ' . $error_message);
     699        }
     700
     701        $decoded = json_decode($response_body, true);
     702
     703        // Check if the response indicates a server-side error even with 200 status
     704        if (!$decoded || (isset($decoded['error']) && $decoded['error'])) {
     705            $error_msg = isset($decoded['error']) ? $decoded['error'] : 'Invalid response from server';
     706            error_log("Post2Podcast API Error: " . $error_msg);
     707            return new WP_Error('generation_failed', $error_msg);
     708        }
     709
     710        return $decoded;
    695711    }
    696712
  • post2podcast/tags/1.2.7/post2podcast.php

    r3437853 r3437893  
    44 * Plugin server URI: https://github.com/samukbg/post2podcast-server
    55 * Description: Convert WordPress posts into podcast episodes with AI voices
    6  * Version: 1.2.6
     6 * Version: 1.2.7
    77 * Author: Samuel Bezerra
    88 * Author URI: https://github.com/samukbg
     
    2020
    2121// Define plugin constants
    22 define('POST2PODCAST_VERSION', '1.2.6');
     22define('POST2PODCAST_VERSION', '1.2.7');
    2323define('POST2PODCAST_PLUGIN_DIR', plugin_dir_path(__FILE__));
    2424define('POST2PODCAST_PLUGIN_URL', plugin_dir_url(__FILE__));
  • post2podcast/tags/1.2.7/readme.txt

    r3437853 r3437893  
    55Tested up to: 6.8
    66Requires PHP: 7.4
    7 Stable tag: 1.2.6
     7Stable tag: 1.2.7
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    274274== Changelog ==
    275275
     276= 1.2.7 =
     277* FIXED: Error handling now properly detects and reports server failures instead of showing 100% progress
     278* FIXED: Language detection now automatically selects appropriate Kokoro TTS voices in dropdown menus
     279* IMPROVED: Automatic server fallback - when one TTS server fails, automatically tries the next available server
     280* IMPROVED: Added 3-minute timeout detection to catch when server stops responding during generation
     281* IMPROVED: Better error messages showing specific failure reasons (connection errors, server errors, timeouts)
     282* IMPROVED: Progress bar now correctly stops on errors instead of completing to 100%
     283* ENHANCED: Backend now uses Kokoro TTS voice codes for all language-specific voice recommendations
     284
    276285= 1.2.6 =
    277286* IMPROVED: Minor bug fixes and performance improvements.
  • post2podcast/trunk/assets/js/admin.js

    r3331992 r3437893  
    1414    var generationInProgress = false;
    1515    var progressInterval = null; // For simulated progress
     16    var lastMessageTime = null; // Track when we last received a message
     17    var timeoutCheckInterval = null; // For checking message timeout
    1618   
    1719    // Handle delete podcast button click (for existing audio)
     
    3941            eventSource = null;
    4042        }
    41        
     43
    4244        if (progressInterval) {
    4345            clearInterval(progressInterval);
    4446            progressInterval = null;
    4547        }
    46        
     48
     49        if (timeoutCheckInterval) {
     50            clearInterval(timeoutCheckInterval);
     51            timeoutCheckInterval = null;
     52        }
     53
    4754        generationInProgress = false;
    4855       
     
    144151                if (response.success) {
    145152                    var detection = response.data.detection_result;
    146                    
     153
    147154                    if (detection.language_code) {
    148155                        // Update the UI with detected language
    149                         var languageInfo = '<p><strong>Detected Language:</strong> ' + 
    150                                          (detection.language_name || detection.language_code) + 
     156                        var languageInfo = '<p><strong>Detected Language:</strong> ' +
     157                                         (detection.language_name || detection.language_code) +
    151158                                         ' <span class="language-code">(' + detection.language_code + ')</span></p>';
    152                         languageInfo += '<p class="description">Voices will be automatically selected based on detected language.</p>';
    153                        
     159
    154160                        $('#detected-language-info').html(languageInfo);
    155                        
    156                         // Show suggested voices if available
     161
     162                        // Automatically select suggested voices if available
    157163                        if (detection.suggested_voices) {
    158164                            console.log('Suggested voices for ' + detection.language_code + ':', detection.suggested_voices);
     165
     166                            var voice1 = detection.suggested_voices.voice1;
     167                            var voice2 = detection.suggested_voices.voice2;
     168
     169                            // Set the voice selectors to the suggested voices
     170                            var $voice1Select = $('#post2podcast_voice1');
     171                            var $voice2Select = $('#post2podcast_voice2');
     172
     173                            // Check if the voices exist in the dropdown and select them
     174                            if ($voice1Select.find('option[value="' + voice1 + '"]').length > 0) {
     175                                $voice1Select.val(voice1);
     176                                console.log('Selected voice1: ' + voice1);
     177                            } else {
     178                                console.warn('Voice ' + voice1 + ' not found in dropdown');
     179                            }
     180
     181                            if ($voice2Select.find('option[value="' + voice2 + '"]').length > 0) {
     182                                $voice2Select.val(voice2);
     183                                console.log('Selected voice2: ' + voice2);
     184                            } else {
     185                                console.warn('Voice ' + voice2 + ' not found in dropdown');
     186                            }
     187
     188                            // Update the language info to show selected voices
     189                            languageInfo += '<p class="description" style="color: #28a745; font-weight: 500;">✓ Voices automatically selected: ' +
     190                                          voice1 + ' and ' + voice2 + '</p>';
     191                            $('#detected-language-info').html(languageInfo);
     192                        } else {
     193                            languageInfo += '<p class="description">Please select appropriate voices for this language.</p>';
     194                            $('#detected-language-info').html(languageInfo);
    159195                        }
    160196                    } else {
     
    230266            eventSource.close();
    231267        }
    232        
    233         // Create a timestamp for this generation session
    234         var sessionId = 'session-' + Date.now();
    235        
     268
     269        // Clear any existing timeout check
     270        if (timeoutCheckInterval) {
     271            clearInterval(timeoutCheckInterval);
     272        }
     273
    236274        // Start Server-Sent Events connection
    237275        try {
     
    256294           
    257295            eventSource = new EventSource(url);
    258            
     296
    259297            eventSource.onopen = function() {
    260298                addStatusMessage('Connected to generation service', 'info');
     299                lastMessageTime = Date.now();
     300
     301                // Start timeout monitoring - if no message for 3 minutes, assume something went wrong
     302                timeoutCheckInterval = setInterval(function() {
     303                    var timeSinceLastMessage = Date.now() - lastMessageTime;
     304                    var maxIdleTime = 180000; // 3 minutes in milliseconds
     305
     306                    if (timeSinceLastMessage > maxIdleTime && generationInProgress) {
     307                        addStatusMessage('❌ Server stopped responding. The generation may have failed.', 'error');
     308                        if (progressInterval) clearInterval(progressInterval);
     309                        if (timeoutCheckInterval) clearInterval(timeoutCheckInterval);
     310                        if (eventSource) {
     311                            eventSource.close();
     312                            eventSource = null;
     313                        }
     314                        resetUI(false);
     315                    }
     316                }, 10000); // Check every 10 seconds
    261317            };
    262            
     318
    263319            eventSource.onmessage = function(event) {
     320                lastMessageTime = Date.now(); // Update last message time
    264321                try {
    265322                    var data = JSON.parse(event.data);
     
    272329                        $progressBar.css('width', '100%').text('100%');
    273330                        addStatusMessage('✓ ' + data.message, 'success');
    274                        
     331
    275332                        // Show the audio preview instead of reloading
    276333                        if (data.audio_url) {
     
    278335                            addStatusMessage('Audio preview is ready!', 'success');
    279336                        }
    280                        
     337
    281338                        // Update credit display after successful generation
    282339                        updateCreditDisplay();
    283                        
     340
    284341                        // Instead of full resetUI, specifically manage button visibility
    285342                        generationInProgress = false; // Allow new actions
     
    293350
    294351                        // Explicitly close the connection on success from client-side
     352                        if (timeoutCheckInterval) clearInterval(timeoutCheckInterval);
    295353                        if (eventSource) {
    296354                            addStatusMessage('Closing SSE connection from client after success.', 'info');
     
    298356                            eventSource = null; // Prevent resetUI or onerror from trying to close it again
    299357                        }
    300                        
     358
    301359                    } else if (data.status === 'error') {
     360                        // Stop the simulated progress immediately on error
     361                        if (progressInterval) clearInterval(progressInterval);
     362                        if (timeoutCheckInterval) clearInterval(timeoutCheckInterval);
     363                        // Do NOT set progress to 100% on error
    302364                        addStatusMessage('❌ Error: ' + data.message, 'error');
    303                         resetUI(true); // Pass true to indicate success, so progress bar stays at 100% briefly
     365                        resetUI(false); // Pass false to indicate error - this will hide progress bar and reset UI
    304366                    }
    305367                   
     
    314376            eventSource.onerror = function(error) {
    315377                console.error('SSE Error:', error);
     378                // Stop the simulated progress immediately
     379                if (progressInterval) clearInterval(progressInterval);
     380                if (timeoutCheckInterval) clearInterval(timeoutCheckInterval);
     381
    316382                if (eventSource.readyState === EventSource.CLOSED) {
    317                     addStatusMessage('Connection to server was closed', 'error');
     383                    addStatusMessage('❌ Connection to server was closed. The server may have encountered an error.', 'error');
     384                } else if (eventSource.readyState === EventSource.CONNECTING) {
     385                    addStatusMessage('❌ Connection error while attempting to reconnect. Please try again.', 'error');
    318386                } else {
    319                     addStatusMessage('Connection error. Please try again.', 'error');
     387                    addStatusMessage('❌ Connection error. Please check your internet connection and try again.', 'error');
    320388                }
    321                 if (progressInterval) clearInterval(progressInterval);
     389
    322390                resetUI(false); // Pass false to indicate error/connection close
    323391            };
     
    366434    }
    367435   
    368     function resetUI(isSuccess) { 
     436    function resetUI(isSuccess) {
    369437        generationInProgress = false;
    370438        $spinner.removeClass('is-active');
    371         if (progressInterval) clearInterval(progressInterval);
     439        if (progressInterval) {
     440            clearInterval(progressInterval);
     441            progressInterval = null;
     442        }
     443        if (timeoutCheckInterval) {
     444            clearInterval(timeoutCheckInterval);
     445            timeoutCheckInterval = null;
     446        }
    372447
    373448        if (!isSuccess) { // Only hide progress bar immediately if not a success case
     
    377452        // On success, the bar is at 100%. It will be hidden/reset if user navigates or starts new generation.
    378453
    379         var audioExists = ($('#preview-audio-source').attr('src') && $('#preview-audio-source').attr('src') !== '') || 
     454        var audioExists = ($('#preview-audio-source').attr('src') && $('#preview-audio-source').attr('src') !== '') ||
    380455                          ($('.post2podcast-player audio source').length > 0 && $('.post2podcast-player audio source').attr('src') !== '');
    381456
     
    389464             $('#delete_podcast').hide();
    390465        }
    391        
     466
    392467        if (eventSource) {
    393468            eventSource.close();
     
    458533            eventSource.close();
    459534        }
     535        if (timeoutCheckInterval) {
     536            clearInterval(timeoutCheckInterval);
     537        }
    460538    });
    461539   
  • post2podcast/trunk/includes/class-post2podcast-api.php

    r3437853 r3437893  
    328328                }
    329329                if (!isset($job_response['audio_content'])) {
    330                     throw new Exception('Audio content not found in API response.');
     330                    $error_detail = isset($job_response['detail']) ? $job_response['detail'] : 'Unknown error';
     331                    throw new Exception('Audio generation failed on server: ' . $error_detail);
    331332                }
    332333                $audio_content = base64_decode($job_response['audio_content']);
     
    682683            $error_message = $response->get_error_message();
    683684            error_log("Post2Podcast WP HTTP API Error (post2podcast_start_sync_generation): " . $error_message);
    684             throw new Exception('API request failed (WP HTTP API error): ' . esc_html($error_message));
     685            return new WP_Error('connection_error', 'Connection error: ' . $error_message);
    685686        }
    686687
     
    690691        if ($status_code >= 400) {
    691692            error_log("Post2Podcast API Error (post2podcast_start_sync_generation): Status " . $status_code . " - Response: " . $response_body);
    692             throw new Exception('API request failed with status code ' . esc_html($status_code) . ': ' . esc_html($response_body));
    693         }
    694         return json_decode($response_body, true);
     693
     694            // Try to parse error details from response
     695            $error_data = json_decode($response_body, true);
     696            $error_message = isset($error_data['detail']) ? $error_data['detail'] : $response_body;
     697
     698            return new WP_Error('api_error', 'Server error (' . $status_code . '): ' . $error_message);
     699        }
     700
     701        $decoded = json_decode($response_body, true);
     702
     703        // Check if the response indicates a server-side error even with 200 status
     704        if (!$decoded || (isset($decoded['error']) && $decoded['error'])) {
     705            $error_msg = isset($decoded['error']) ? $decoded['error'] : 'Invalid response from server';
     706            error_log("Post2Podcast API Error: " . $error_msg);
     707            return new WP_Error('generation_failed', $error_msg);
     708        }
     709
     710        return $decoded;
    695711    }
    696712
  • post2podcast/trunk/post2podcast.php

    r3437853 r3437893  
    44 * Plugin server URI: https://github.com/samukbg/post2podcast-server
    55 * Description: Convert WordPress posts into podcast episodes with AI voices
    6  * Version: 1.2.6
     6 * Version: 1.2.7
    77 * Author: Samuel Bezerra
    88 * Author URI: https://github.com/samukbg
     
    2020
    2121// Define plugin constants
    22 define('POST2PODCAST_VERSION', '1.2.6');
     22define('POST2PODCAST_VERSION', '1.2.7');
    2323define('POST2PODCAST_PLUGIN_DIR', plugin_dir_path(__FILE__));
    2424define('POST2PODCAST_PLUGIN_URL', plugin_dir_url(__FILE__));
  • post2podcast/trunk/readme.txt

    r3437853 r3437893  
    55Tested up to: 6.8
    66Requires PHP: 7.4
    7 Stable tag: 1.2.6
     7Stable tag: 1.2.7
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    274274== Changelog ==
    275275
     276= 1.2.7 =
     277* FIXED: Error handling now properly detects and reports server failures instead of showing 100% progress
     278* FIXED: Language detection now automatically selects appropriate Kokoro TTS voices in dropdown menus
     279* IMPROVED: Automatic server fallback - when one TTS server fails, automatically tries the next available server
     280* IMPROVED: Added 3-minute timeout detection to catch when server stops responding during generation
     281* IMPROVED: Better error messages showing specific failure reasons (connection errors, server errors, timeouts)
     282* IMPROVED: Progress bar now correctly stops on errors instead of completing to 100%
     283* ENHANCED: Backend now uses Kokoro TTS voice codes for all language-specific voice recommendations
     284
    276285= 1.2.6 =
    277286* IMPROVED: Minor bug fixes and performance improvements.
Note: See TracChangeset for help on using the changeset viewer.