From ff4b9d9e997e8cb754e37b521cc475df62ebc5a7 Mon Sep 17 00:00:00 2001 From: erew123 <35898566+erew123@users.noreply.github.com> Date: Mon, 8 Jan 2024 00:56:36 +0000 Subject: [PATCH] Updates to reduce browser load Pagination of files list. No Playback option Better clearing of played back audio from memory --- .../tts_chunk_player/tts_chunk_player.html | 413 +++++++++++++++--- 1 file changed, 345 insertions(+), 68 deletions(-) diff --git a/templates/tts_chunk_player/tts_chunk_player.html b/templates/tts_chunk_player/tts_chunk_player.html index 21d3c6d..41082dc 100644 --- a/templates/tts_chunk_player/tts_chunk_player.html +++ b/templates/tts_chunk_player/tts_chunk_player.html @@ -175,27 +175,32 @@ } .progress-overlay { - position: absolute; - top: 10px; - right: 10px; - background: rgba(255, 255, 255, 0.9); - border-radius: 5px; - padding: 10px; - display: flex; - flex-direction: column; /* Stack items vertically */ - align-items: center; /* Center items horizontally within the container */ - justify-content: center; /* Center items vertically within the container */ - z-index: 10; -} + position: absolute; + top: 10px; + right: 10px; + background: rgba(255, 255, 255, 0.9); + border-radius: 5px; + padding: 10px; + display: flex; + flex-direction: column; + /* Stack items vertically */ + align-items: center; + /* Center items horizontally within the container */ + justify-content: center; + /* Center items vertically within the container */ + z-index: 10; + } -/* Ensure the text is wrapped in a container with a class, e.g., .progress-text */ -.progress-text { - text-align: right; /* Center the text horizontally */ - margin-top: 10px; /* Add space between the spinner and the text */ -} + /* Ensure the text is wrapped in a container with a class, e.g., .progress-text */ + .progress-text { + text-align: right; + /* Center the text horizontally */ + margin-top: 10px; + /* Add space between the spinner and the text */ + } -#progressIndicator { + #progressIndicator { /* Adjust this if you want to position the spinning disk differently */ display: inline-block; margin-left: 20px; @@ -413,6 +418,78 @@ margin-left: 45px; /* Adjust the value as needed */ } + + /* Pagination Buttons Styling */ + .pagination-btn { + padding: 5px 10px; + /* Adjust padding to reduce height */ + font-size: 0.8em; + /* Adjust font size for smaller buttons */ + line-height: 1; + /* Adjust line height to match the smaller size */ + margin: 2px; + /* Optional: adjust spacing between buttons */ + } + + /* Smaller input field for jump-to-page */ + .pagination-input { + height: 20px; + /* Adjust height for smaller input field */ + padding: 2px 5px; + /* Adjust padding for smaller input field */ + font-size: 0.8em; + /* Adjust font size for smaller input field */ + line-height: 1; + /* Adjust line height to match the smaller size */ + vertical-align: middle; + /* Align with buttons if necessary */ + } + + /* Apply the styles to your pagination controls */ + #paginationControls button { + /* Apply the button styling */ + height: auto; + /* Let padding control the height */ + min-height: 20px; + /* Set a minimum height if needed */ + } + + #paginationControls input[type="number"] { + /* Apply the input styling */ + } + + /* Smaller input field for jump-to-page */ + .pagination-input { + height: 2px; + /* Reduce the height */ + padding: 0px 0px; + /* Reduce the padding to decrease overall height */ + font-size: 0.8em; + /* Optionally reduce the font size */ + margin-bottom: 13px; + margin-top: 2px; + border: 0px solid #ccc; + /* Adjust border styling if needed */ + line-height: normal; + /* Adjust line height to match the new size */ + vertical-align: top; + /* Align vertically with buttons */ + } + + /* Apply the styles to your pagination controls */ + #paginationControls1 button { + /* Apply the button styling */ + height: auto; + /* Let padding control the height */ + min-height: 20px; + /* Set a minimum height if needed */ + margin-top: 15px + } + + #paginationControls1 input[type="number"] { + /* Apply the input styling */ + margin-top: 15px + } @@ -469,6 +546,7 @@ @@ -524,9 +602,23 @@ +
+ + Page 1 of X + + + +
+
+ + Page 1 of X + + + +
@@ -537,35 +629,35 @@ import Crunker from './crunker.esm.js'; async function exportMP3() { - const crunker = new Crunker(); - const fileUrls = ttsList.map(item => item.fileUrl); - const progressIndicator = document.getElementById('progressIndicator'); - const progressText = document.getElementById('progressText'); + const crunker = new Crunker(); + const fileUrls = ttsList.map(item => item.fileUrl); + const progressIndicator = document.getElementById('progressIndicator'); + const progressText = document.getElementById('progressText'); - // Show progress indicator and update text - if (progressIndicator && progressText) { - progressText.textContent = 'Export'; - progressText.style.color = 'black'; // Set the text color to black - progressIndicator.style.display = 'block'; - } - - try { - const audioBuffers = await crunker.fetchAudio(...fileUrls); - const combined = crunker.concatAudio(audioBuffers); - const output = crunker.export(combined, "audio/wav"); - crunker.download(output.blob, "combined_audio"); // This will download the file as 'combined_audio.mp3' - } catch (error) { - console.error('Error combining files:', error); - if (progressText) { - progressText.textContent = 'Error during export!'; + // Show progress indicator and update text + if (progressIndicator && progressText) { + progressText.textContent = 'Export'; + progressText.style.color = 'black'; // Set the text color to black + progressIndicator.style.display = 'block'; } - } finally { - // Hide progress indicator after completion or error - if (progressIndicator) { - progressIndicator.style.display = 'none'; + + try { + const audioBuffers = await crunker.fetchAudio(...fileUrls); + const combined = crunker.concatAudio(audioBuffers); + const output = crunker.export(combined, "audio/wav"); + crunker.download(output.blob, "combined_audio"); // This will download the file as 'combined_audio.mp3' + } catch (error) { + console.error('Error combining files:', error); + if (progressText) { + progressText.textContent = 'Error during export!'; + } + } finally { + // Hide progress indicator after completion or error + if (progressIndicator) { + progressIndicator.style.display = 'none'; + } } } -} // Attach exportMP3 to the window object to make it globally accessible window.exportMP3 = exportMP3; @@ -582,7 +674,7 @@ let audioQueue = []; let currentlyPlayingIndex = 0; let isPlaying = false; - let playModeValue = 'local'; + let playModeValue = localStorage.getItem('playMode') || 'local'; let currentSound = null; let preloadThreshold = 2; // Number of files to preload before starting playback let isStreamInitialized = false; @@ -591,6 +683,9 @@ const howlInstances = {}; let listPlaying = false; let listPlayIndex = 0; + let currentPage = 1; + let itemsPerPage = 100; // Adjust as needed + //////////////////////////////////// //////// Progress Indicator //////// @@ -602,7 +697,7 @@ if (progressIndicator && generationProgress) { let message = ''; - console.log("PlayType is:", playType); + //console.log("PlayType is:", playType); if (playType === 'stream') { // When streaming, display a different message message = '      Streaming...'; @@ -650,6 +745,7 @@ const volume = parseFloat(document.getElementById("volume").value); const chunkSize = parseInt(document.getElementById("chunkSize").value); const playType = document.getElementById('playType').value; // Play type (chunk or stream) + playModeValue = document.getElementById('playMode').value; let cleanedText = textInput.replace(/ \- | \– /g, ' '); cleanedText = cleanedText.replace(/%/g, " percent"); @@ -685,9 +781,14 @@ if (stopTTSGeneration) break; showProgress(i + 1, totalChunks, playType); if (playModeValue === 'local') { + // Generate the chunk and play it locally await generateAndCache(chunks[i], language, characterVoice, volume); - } else { + } else if (playModeValue === 'remote') { + // Generate the chunk and play it remotely await generateAndPlayRemote(chunks[i], language, characterVoice, volume); + } else if (playModeValue === 'none') { + // Generate the chunk but do not play it + await generateWithoutPlayback(chunks[i], language, characterVoice); } } } @@ -735,6 +836,7 @@ const result = await response.json(); if (result.output_file_url) { + //console.log('TTS Generation Result:', result); // If an ID was passed in, we're regenerating an existing item if (id !== null) { let itemIndex = ttsList.findIndex(item => item.id === id); @@ -748,6 +850,15 @@ // No ID was passed in, so we add a new item to the list addToTTSList(result.output_file_url, chunk, characterVoice, language); } + const newPage = Math.ceil(ttsList.length / itemsPerPage); + //console.log('New Page Calculated:', newPage); + if (currentPage !== newPage) { + currentPage = newPage; // Move to the new page where the item was added + //console.log('Updating Current Page to:', currentPage); + } + + // Refresh the list display to reflect the new pagination state + updateUIList(); // Update the audio queue and UI const sound = new Howl({ @@ -769,6 +880,56 @@ } } + async function generateWithoutPlayback(chunk, language, characterVoice, id = null) { + // Perform the generation process as usual but do not play the audio + try { + const data = new URLSearchParams({ + text_input: chunk.trim(), + text_filtering: "standard", + character_voice_gen: characterVoice, + narrator_enabled: "false", + narrator_voice_gen: characterVoice, + text_not_inside: "character", + language: language, + output_file_name: "TTS", + output_file_timestamp: "true", + autoplay: "false", + autoplay_volume: "0.1" + }); + const response = await fetch("http://127.0.0.1:7851/api/tts-generate", { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: data, + }); + const result = await response.json(); + if (result.output_file_url) { + // If an ID was passed in, we're regenerating an existing item + if (id !== null) { + let itemIndex = ttsList.findIndex(item => item.id === id); + if (itemIndex !== -1) { + ttsList[itemIndex].fileUrl = result.output_file_url; + // Other properties can be updated as needed, e.g., in case the language or voice has changed + ttsList[itemIndex].language = language; + ttsList[itemIndex].characterVoice = characterVoice; + } + } else { + // No ID was passed in, so we add a new item to the list + addToTTSList(result.output_file_url, chunk, characterVoice, language); + } + const newPage = Math.ceil(ttsList.length / itemsPerPage); + if (currentPage !== newPage) { + currentPage = newPage; // Move to the new page where the item was added + } + updateUIList(); + } + } catch (error) { + console.error("Error in generateWithoutPlayback:", error); + } + } + + function addToTTSList(fileUrl, text, characterVoice, language) { try { ttsList.push({ @@ -784,6 +945,7 @@ } } + async function regenerateTTS(id) { // Find the details of the TTS to regenerate const ttsDetails = ttsList.find(item => item.id === id); @@ -804,7 +966,13 @@ } listDisplay.innerHTML = ''; // Clear the current list - ttsList.forEach(item => { + // Determine the range of items to display for pagination + const start = (currentPage - 1) * itemsPerPage; + const end = start + itemsPerPage; + const paginatedItems = ttsList.slice(start, end); + + // Add items to the display for the current page + paginatedItems.forEach(item => { // Extract just the file name from the URL const wavFileName = item.fileUrl.split('/').pop(); @@ -814,30 +982,36 @@ const entryDiv = document.createElement('div'); entryDiv.id = `item-${item.id}`; // Assign an ID to the div for highlighting entryDiv.classList.add('ttsListItem'); // Add a class for styling - entryDiv.id = `item-${item.id}`; // Make sure this is unique and correct // Update the Play/Stop button's onclick to call a function that handles playing and highlighting entryDiv.innerHTML = ` - - - ID: ${item.id} - Voice: ${item.characterVoice} - Language: ${item.language} - Wav: ${wavFileName} - - ${displayText} + + + ID: ${item.id} + Voice: ${item.characterVoice} + Language: ${item.language} + Wav: ${wavFileName} + + ${displayText} `; listDisplay.appendChild(entryDiv); }); + + // Update pagination controls + const lastPage = Math.ceil(ttsList.length / itemsPerPage); + document.getElementById('pageIndicator').innerText = `Page ${currentPage} of ${lastPage}`; + document.getElementById('pageIndicator1').innerText = `Page ${currentPage} of ${lastPage}`; + document.getElementById('prevPage').disabled = (currentPage === 1); + document.getElementById('nextPage').disabled = (currentPage === lastPage); } + async function generateAndPlayRemote(chunk, language, characterVoice, volume) { try { if (stopTTSGeneration) { console.log("TTS generation stopped."); return; // Early return if stop flag is set } - console.log("generateAndPlayRemote:", chunk); const data = new URLSearchParams({ text_input: chunk.trim(), text_filtering: "standard", @@ -913,13 +1087,10 @@ src: [fileUrl], volume: parseFloat(document.getElementById("volume").value), onplay: function () { - console.log('Playback started for', fileUrl); }, onend: function () { - console.log('Playback finished for', fileUrl); }, onpause: function () { - console.log('Playback paused for', fileUrl); }, onloaderror: function (e) { console.error('Load error for', fileUrl, ':', e); @@ -1054,15 +1225,26 @@ function playNextLocalChunk() { if (audioQueue.length > 0 && currentlyPlayingIndex < audioQueue.length) { isPlaying = true; - audioQueue[currentlyPlayingIndex++].play(); + let currentSound = audioQueue[currentlyPlayingIndex]; + currentSound.play(); + + // Once a sound finishes playing, unload it + currentSound.on('end', () => { + currentSound.unload(); + }); + + currentlyPlayingIndex++; } else { isPlaying = false; } } + function stopPlayback() { Howler.stop(); audioQueue = []; + // Unload each sound in the queue + audioQueue.forEach(sound => sound.unload()); currentlyPlayingIndex = 0; isPlaying = false; } @@ -1102,7 +1284,6 @@ const startFromIdElement = document.getElementById('startFromId'); if (startFromIdElement) { startFromIdElement.setAttribute('value', '1'); - console.log('Start From ID set to 1'); } // Other initializations... }); @@ -1122,6 +1303,7 @@ // and highlight the corresponding item playListFromIndex(startIndex); highlightPlaying(startIndex + 1); // Assuming the list items are 1-indexed + updateCurrentPage(startIndex + 1); // Update the current page based on the playing item } else { // If list is set to pause, pause the current item // and remove highlighting @@ -1154,6 +1336,26 @@ if (currentItem) { currentItem.classList.add('highlight'); } + updateCurrentPage(id); + } + + + function updateCurrentPage(itemIndex) { + // Calculate the page number based on the item index + // Note: Ensure that the itemIndex is 1-based; if it's 0-based, add 1. + const newPage = Math.ceil(itemIndex / itemsPerPage); + + // Update the current page and UI if the new page is different from the current page + if (currentPage !== newPage) { + currentPage = newPage; // Set the new current page + updateUIList(); // Refresh the list display to show the correct page + } + + // Optionally, update the 'Jump to Page' input to reflect the current page + const jumpToPageInput = document.getElementById('jumpToPage'); + if (jumpToPageInput) { + jumpToPageInput.value = currentPage; + } } @@ -1191,6 +1393,7 @@ console.log('Index is out of bounds, stopping playback.'); // Clear any highlights if out of bounds highlightPlaying(null); + updateUIList(); } } @@ -1212,6 +1415,14 @@ if (startFromIdElement) { startFromIdElement.value = '1'; } + const pageIndicator = document.getElementById('pageIndicator'); + if (pageIndicator) { + pageIndicator.textContent = 'Page 1 of 1'; + } + const pageIndicator1 = document.getElementById('pageIndicator1'); + if (pageIndicator1) { + pageIndicator1.textContent = 'Page 1 of 1'; + } } function editText(id) { @@ -1424,6 +1635,70 @@ //////// General Dcoument Listeners //////// //////////////////////////////////////////// + // Event listeners for pagination controls + document.getElementById('prevPage').addEventListener('click', () => { + if (currentPage > 1) { + currentPage--; + updateUIList(); + } + }); + + document.getElementById('nextPage').addEventListener('click', () => { + const lastPage = Math.ceil(ttsList.length / itemsPerPage); + if (currentPage < lastPage) { + currentPage++; + updateUIList(); + } + }); + + // Initialize the list on page load + document.addEventListener('DOMContentLoaded', updateUIList); + + + // For the 'Previous' button at the bottom + document.getElementById('prevPage1').addEventListener('click', () => { + if (currentPage > 1) { + currentPage--; + updateUIList(); + } + }); + + // For the 'Next' button at the bottom + document.getElementById('nextPage1').addEventListener('click', () => { + let lastPage = Math.ceil(ttsList.length / itemsPerPage); + if (currentPage < lastPage) { + currentPage++; + updateUIList(); + } + }); + + function handleJumpButtonClick(event) { + // Determine which set of controls to use based on the button that was clicked + const suffix = event.target.id === 'jumpButton' ? '' : '1'; + const jumpToInput = document.getElementById(`jumpToPage${suffix}`); + const jumpToValue = jumpToInput.value.trim(); // Get the trimmed value + const jumpTo = parseInt(jumpToValue, 10); // Parse the value as an integer + + if (!isNaN(jumpTo)) { + let lastPage = Math.ceil(ttsList.length / itemsPerPage); + + if (jumpTo >= 1 && jumpTo <= lastPage) { + currentPage = jumpTo; + updateUIList(); + } else { + alert(`Please enter a valid page number between 1 and ${lastPage}`); + } + } else { + alert("Please enter a number to jump to."); + } + } + + // Set up the event listener for both jump buttons + document.getElementById('jumpButton').addEventListener('click', handleJumpButtonClick); + document.getElementById('jumpButton1').addEventListener('click', handleJumpButtonClick); + + + // Event listener for changing play type document.getElementById('playType').addEventListener('change', function () { isStreamingMode = this.value === 'stream'; @@ -1545,11 +1820,13 @@ adjustPlaybackAndChunkSize(savedPlayType); // Adjust selectability on page load } - // Initialize playback based on preference - const savedPlayMode = localStorage.getItem('playMode'); - if (savedPlayMode) { - document.getElementById('playMode').value = savedPlayMode; - } + document.addEventListener('DOMContentLoaded', function () { + const savedPlayMode = localStorage.getItem('playMode'); + if (savedPlayMode) { + document.getElementById('playMode').value = savedPlayMode; + } + playModeValue = savedPlayMode || 'local'; // Default to 'local' if nothing is saved + }); // Initialize chunk size based on preference const savedChunkSize = localStorage.getItem('chunkSize'); @@ -1665,4 +1942,4 @@ - + \ No newline at end of file