From 2dd303f53c00211d2e76d0fbfa5c40412b7c629a Mon Sep 17 00:00:00 2001 From: erew123 <35898566+erew123@users.noreply.github.com> Date: Mon, 1 Jan 2024 18:10:14 +0000 Subject: [PATCH] Add files via upload --- script.py | 145 +- templates/TTS_Input_Proof_of_concept.html | 584 +++++++ templates/admin.html | 1791 +++++++++++++-------- tts_server.py | 212 ++- 4 files changed, 1918 insertions(+), 814 deletions(-) create mode 100644 templates/TTS_Input_Proof_of_concept.html diff --git a/script.py b/script.py index 3e6d98e..a3d6eab 100644 --- a/script.py +++ b/script.py @@ -509,11 +509,53 @@ def voice_preview(string): return f"[{params['branding']}Server] Audio generation failed. Status: {generate_response.get('status')}" -#def replace_asterisk(match): -# return match.group(0) + '"<*' +def process_text(text): + # Normalize HTML encoded quotes + text = html.unescape(text) -def replace_asterisk(match): - return '"<*' + # Replace ellipsis with a single dot + text = re.sub(r'\.{3,}', '.', text) + + # Pattern to identify combined narrator and character speech + combined_pattern = r'(\*[^*"]+\*|"[^"*]+")' + + # List to hold parts of speech along with their type + ordered_parts = [] + + # Track the start of the next segment + start = 0 + + # Find all matches + for match in re.finditer(combined_pattern, text): + # Add the text before the match, if any, as ambiguous + if start < match.start(): + ambiguous_text = text[start:match.start()].strip() + if ambiguous_text: + ordered_parts.append(('ambiguous', ambiguous_text)) + + # Add the matched part as either narrator or character + matched_text = match.group(0) + if matched_text.startswith('*') and matched_text.endswith('*'): + ordered_parts.append(('narrator', matched_text.strip('*').strip())) + elif matched_text.startswith('"') and matched_text.endswith('"'): + ordered_parts.append(('character', matched_text.strip('"').strip())) + else: + # In case of mixed or improperly formatted parts + if '*' in matched_text: + ordered_parts.append(('narrator', matched_text.strip('*').strip('"'))) + else: + ordered_parts.append(('character', matched_text.strip('"').strip('*'))) + + # Update the start of the next segment + start = match.end() + + # Add any remaining text after the last match as ambiguous + if start < len(text): + ambiguous_text = text[start:].strip() + if ambiguous_text: + ordered_parts.append(('ambiguous', ambiguous_text)) + + return ordered_parts ################################# #### TTS STANDARD GENERATION #### @@ -534,86 +576,55 @@ def output_modifier(string, state): if process_lock.acquire(blocking=False): try: if params["narrator_enabled"]: - # Do Some basic stripping and remove any double CR's - processed_string = ( - original_string - .replace("***", "*") - .replace("**", "*") - .replace("\n\n", "\n") - .replace("\n", "") - ) - processed_string = re.sub(r'\.\*\n\*', '. ', processed_string) - processed_string = (processed_string .replace('"\n\n*', '"<*')) - processed_string = (processed_string .replace('"\n', '< ')) - processed_string = (processed_string .replace("'", "'")) - processed_string = (processed_string .replace("\n", " ")) - processed_string = (processed_string .replace('"', '"<')) - processed_string = (processed_string .replace('"', '"<')) - #pattern = re.compile(r'(?<=[a-zA-Z!])"<(?=[a-zA-Z])') - pattern = re.compile(r'(?<=[a-zA-Z!?\.])"<(?=[a-zA-Z])') - processed_string = pattern.sub('" ', processed_string) - pattern = re.compile(r'(?<=[a-zA-Z])\*(?=[a-zA-Z])') - processed_string = pattern.sub('*"', processed_string) - processed_string = processed_string.replace('"<. *', '"< *"') - processed_string = processed_string.replace('< *"', '< *') - processed_string = processed_string.replace('. *', '< *') - processed_string = processed_string.replace('! *', '"< *') - processed_string = processed_string.replace('? *', '< *') - processed_string = processed_string.replace('* ', '* "<') - processed_string = processed_string.replace('*. ', '* "<') - processed_string = re.sub(r'\*(?=[a-zA-Z])', replace_asterisk, processed_string) - #print("PROCESSED STRING IS:", processed_string) - # Set up a tracking of the individual wav files. + processed_parts = process_text(original_string) + audio_files_all_paragraphs = [] - # Split the line using " and ".* " (so end of sentences, leaving special characters added to the start of all OTHER sentences, bar possibly the first one if its starting with a * - parts = re.split(r'"|\.\*', processed_string) - audio_files_paragraph = [] - for i, part in enumerate(parts): + for part_type, part in processed_parts: # Skip parts that are too short if len(part.strip()) <= 3: continue - # Figure out which type of line it is, then replace characters as necessary to avoid TTS trying to pronunce them, htmlunescape after. - # Character will always be a < with a letter immediately after it - #print("PART IS:", part) - if '<' in part and '< *' not in part and '<*' not in part and '< *' not in part and '< ' not in part and '< ' not in part: - cleaned_part = html.unescape(part.replace('<', '')) - voice_to_use = params["voice"] - #Narrator will always be an * or < with an * a position or two after it. - elif '<*' in part or '< *' in part or '< *' in part or '*' in part: - cleaned_part = html.unescape(part.replace('<*', '').replace('< *', '').replace('< *', '').replace('*', '')) + + # Determine the voice to use based on the part type + if part_type == 'narrator': voice_to_use = params["narrator_voice"] - #If the other two dont capture it, aka, the AI gave no * or " on the line, use non_quoted_text_is aka user interface, user can choose Char or Narrator - elif non_quoted_text_is: - #print("HIT ELIF CHAR VOICE") - cleaned_part = html.unescape(part.replace('< ', '').replace('< ', '').replace('< ', '')) + print(f"[{params['branding']}TTSGen] \033[92mNarrator\033[0m") # Green + elif part_type == 'character': voice_to_use = params["voice"] + print(f"[{params['branding']}TTSGen] \033[36mCharacter\033[0m") # Yellow else: - #print("HIT ELIF NARRATOR VOICE") - cleaned_part = html.unescape(part.replace('< ', '').replace('< ', '').replace('< ', '')) - voice_to_use = params["narrator_voice"] - # Generate that TTS and output to a file + # Handle ambiguous parts based on user preference + voice_to_use = params["voice"] if non_quoted_text_is else params["narrator_voice"] + voice_description = "\033[36mCharacter (Text-not-inside)\033[0m" if non_quoted_text_is else "\033[92mNarrator (Text-not-inside)\033[0m" + print(f"[{params['branding']}TTSGen] {voice_description}") + + # Replace multiple exclamation marks, question marks, or other punctuation with a single instance + cleaned_part = re.sub(r'([!?.])\1+', r'\1', part) + # Further clean to remove any other unwanted characters + cleaned_part = re.sub(r'[^a-zA-Z0-9\s\.,;:!?\-\'"]', '', cleaned_part) + # Remove all newline characters (single or multiple) + cleaned_part = re.sub(r'\n+', ' ', cleaned_part) + + # Generate TTS and output to a file output_filename = get_output_filename(state) generate_response = send_generate_request( cleaned_part, voice_to_use, language_code, output_filename ) audio_path = generate_response.get("data", {}).get("audio_path") - audio_files_paragraph.append(audio_path) - # Accumulate audio files within the paragraph - audio_files_all_paragraphs.extend(audio_files_paragraph) + audio_files_all_paragraphs.append(audio_path) + # Combine audio files across paragraphs final_output_file = combine( audio_files_all_paragraphs, params["output_folder_wav"], state ) else: - processed_string = ( - original_string - .replace("***", "") - .replace("**", "") - .replace("*", "") - .replace("\n\n", "\n") - .replace("'", "'") - ) - cleaned_part = html.unescape(processed_string) + # Decode HTML entities first + cleaned_part = html.unescape(original_string) + # Replace multiple instances of certain punctuation marks with a single instance + cleaned_part = re.sub(r'([!?.])\1+', r'\1', cleaned_part) + # Further clean to remove any other unwanted characters + cleaned_part = re.sub(r'[^a-zA-Z0-9\s\.,;:!?\-\'"]', '', cleaned_part) + # Remove all newline characters (single or multiple) + cleaned_part = re.sub(r'\n+', ' ', cleaned_part) # Process the part and give it a non-character name if being used vai API or standalone. if "character_menu" in state: output_file = Path(f'{params["output_folder_wav"]}/{state["character_menu"]}_{int(time.time())}.wav') diff --git a/templates/TTS_Input_Proof_of_concept.html b/templates/TTS_Input_Proof_of_concept.html new file mode 100644 index 0000000..0cb5333 --- /dev/null +++ b/templates/TTS_Input_Proof_of_concept.html @@ -0,0 +1,584 @@ + + + +
+ + +