| 1010 | 1011 | |
| 1011 | 1012 | /** |
| 1012 | 1013 | * Init voiceMapEntries for character select list. |
| 1014 | + * If an initialization is already in progress, it returns the existing Promise instead of starting a new one. |
| 1013 | 1015 | * @param {boolean} unrestricted - If true, will include all characters in voiceMapEntries, even if they are not in the current chat. |
| 1016 | + * @returns {Promise} A promise that resolves when the initialization is complete. |
| 1014 | 1017 | */ |
| 1015 | 1018 | export async function initVoiceMap(unrestricted = false) { |
| 1019 | + // Preventing parallel execution |
| 1020 | + if (currentInitVoiceMapPromise) { |
| 1021 | + return currentInitVoiceMapPromise; |
| 1022 | + } |
| 1023 | + |
| 1024 | + currentInitVoiceMapPromise = (async () => { |
| 1025 | + const initialChatId = getCurrentChatId(); |
| 1026 | + try { |
| 1027 | + await initVoiceMapInternal(unrestricted); |
| 1028 | + } finally { |
| 1029 | + currentInitVoiceMapPromise = null; |
| 1030 | + } |
| 1031 | + const currentChatId = getCurrentChatId(); |
| 1032 | + |
| 1033 | + if (initialChatId !== currentChatId) { |
| 1034 | + // Chat changed during initialization, reinitialize |
| 1035 | + await initVoiceMap(unrestricted); |
| 1036 | + } |
| 1037 | + })(); |
| 1038 | + |
| 1039 | + return currentInitVoiceMapPromise; |
| 1040 | +} |
| 1041 | + |
| 1042 | +/** |
| 1043 | + * Init voiceMapEntries for character select list. |
| 1044 | + * @param {boolean} unrestricted - If true, will include all characters in voiceMapEntries, even if they are not in the current chat. |
| 1045 | + */ |
| 1046 | +async function initVoiceMapInternal(unrestricted) { |
| 1016 | 1047 | // Gate initialization if not enabled or TTS Provider not ready. Prevents error popups. |
| 1017 | 1048 | const enabled = $('#tts_enabled').is(':checked'); |
| 1018 | 1049 | if (!enabled) { |