From c370b6ecceaa4437de1379ad1e08287b71bb51ca Mon Sep 17 00:00:00 2001 From: Saifeddine ALOUI Date: Sun, 5 May 2024 16:01:19 +0200 Subject: [PATCH] fixed --- configs/config.yaml | 2 +- .../personal/configs/lollms_elf_config.yaml | 2 +- .../personal/configs/lollms_elf_config.yaml | 2 +- .../configs/lollms_elf_local_config.yaml | 2 +- lollms/app.py | 3 +- lollms/configs/config.yaml | 2 +- lollms/personality.py | 4 +- lollms/server/configs/config.yaml | 2 +- .../endpoints/lollms_personalities_infos.py | 3 - lollms/server/endpoints/lollms_sd.py | 43 ++++++-- lollms/server/endpoints/lollms_user.py | 10 +- lollms/server/endpoints/lollms_xtts.py | 102 ++++++++++++++++-- lollms/services/sd/lollms_sd.py | 69 ++++++++---- lollms/services/xtts/lollms_xtts.py | 70 +++++++----- .../configs/lollms_discord_local_config.yaml | 2 +- 15 files changed, 239 insertions(+), 79 deletions(-) diff --git a/configs/config.yaml b/configs/config.yaml index e721782..746e29a 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -81,7 +81,7 @@ auto_show_browser: true copy_to_clipboard_add_all_details: false # Voice service -enable_voice_service: false +xtts_enable: false xtts_base_url: http://localhost:8020 xtts_use_deepspeed: false xtts_use_streaming_mode: true diff --git a/elf_docker_cfg/personal/configs/lollms_elf_config.yaml b/elf_docker_cfg/personal/configs/lollms_elf_config.yaml index 4e2af3f..a52c570 100644 --- a/elf_docker_cfg/personal/configs/lollms_elf_config.yaml +++ b/elf_docker_cfg/personal/configs/lollms_elf_config.yaml @@ -78,7 +78,7 @@ auto_show_browser: true copy_to_clipboard_add_all_details: false # Voice service -enable_voice_service: false +xtts_enable: false xtts_base_url: http://localhost:8020 auto_read: false xtts_current_voice: null diff --git a/elf_test_cfg/personal/configs/lollms_elf_config.yaml b/elf_test_cfg/personal/configs/lollms_elf_config.yaml index 4e2af3f..a52c570 100644 --- a/elf_test_cfg/personal/configs/lollms_elf_config.yaml +++ b/elf_test_cfg/personal/configs/lollms_elf_config.yaml @@ -78,7 +78,7 @@ auto_show_browser: true copy_to_clipboard_add_all_details: false # Voice service -enable_voice_service: false +xtts_enable: false xtts_base_url: http://localhost:8020 auto_read: false xtts_current_voice: null diff --git a/elf_test_cfg/personal/configs/lollms_elf_local_config.yaml b/elf_test_cfg/personal/configs/lollms_elf_local_config.yaml index 4e2af3f..a52c570 100644 --- a/elf_test_cfg/personal/configs/lollms_elf_local_config.yaml +++ b/elf_test_cfg/personal/configs/lollms_elf_local_config.yaml @@ -78,7 +78,7 @@ auto_show_browser: true copy_to_clipboard_add_all_details: false # Voice service -enable_voice_service: false +xtts_enable: false xtts_base_url: http://localhost:8020 auto_read: false xtts_current_voice: null diff --git a/lollms/app.py b/lollms/app.py index 6786dd3..b5af08a 100644 --- a/lollms/app.py +++ b/lollms/app.py @@ -665,7 +665,8 @@ class LollmsApplication(LoLLMsCom): else: conditionning = self.personality._personality_conditioning - conditionning = self.personality.replace_keys(conditionning, self.personality.conditionning_commands) +"" if conditionning[-1]=="\n" else "\n" + if len(conditionning)>0: + conditionning = self.personality.replace_keys(conditionning, self.personality.conditionning_commands) +"" if conditionning[-1]=="\n" else "\n" # Check if there are document files to add to the prompt internet_search_results = "" diff --git a/lollms/configs/config.yaml b/lollms/configs/config.yaml index e721782..746e29a 100644 --- a/lollms/configs/config.yaml +++ b/lollms/configs/config.yaml @@ -81,7 +81,7 @@ auto_show_browser: true copy_to_clipboard_add_all_details: false # Voice service -enable_voice_service: false +xtts_enable: false xtts_base_url: http://localhost:8020 xtts_use_deepspeed: false xtts_use_streaming_mode: true diff --git a/lollms/personality.py b/lollms/personality.py index 5975ed3..fcd1a61 100644 --- a/lollms/personality.py +++ b/lollms/personality.py @@ -13,7 +13,7 @@ from lollms.config import InstallOption, TypedConfig, BaseConfig from lollms.main_config import LOLLMSConfig from lollms.paths import LollmsPaths from lollms.binding import LLMBinding, BindingType -from lollms.utilities import PromptReshaper, PackageManager, discussion_path_to_url, process_ai_output +from lollms.utilities import PromptReshaper, PackageManager, discussion_path_to_url, process_ai_output, remove_text_from_string from lollms.com import NotificationType, NotificationDisplayType from lollms.client_session import Session, Client @@ -702,7 +702,7 @@ class AIPersonality: antiprompt = self.detect_antiprompt(bot_says) if antiprompt: - self.bot_says = self.remove_text_from_string(bot_says,antiprompt) + self.bot_says = remove_text_from_string(bot_says,antiprompt) ASCIIColors.warning(f"\n{antiprompt} detected. Stopping generation") return False else: diff --git a/lollms/server/configs/config.yaml b/lollms/server/configs/config.yaml index e721782..746e29a 100644 --- a/lollms/server/configs/config.yaml +++ b/lollms/server/configs/config.yaml @@ -81,7 +81,7 @@ auto_show_browser: true copy_to_clipboard_add_all_details: false # Voice service -enable_voice_service: false +xtts_enable: false xtts_base_url: http://localhost:8020 xtts_use_deepspeed: false xtts_use_streaming_mode: true diff --git a/lollms/server/endpoints/lollms_personalities_infos.py b/lollms/server/endpoints/lollms_personalities_infos.py index 9ab2ca0..0f28404 100644 --- a/lollms/server/endpoints/lollms_personalities_infos.py +++ b/lollms/server/endpoints/lollms_personalities_infos.py @@ -258,9 +258,6 @@ def remove_file(data:RemoveFileData): # ------------------------------------------- Languages endpoints ------------------------------------------------ -class Identification(BaseModel): - client_id:str - @router.post("/get_personality_languages_list") def get_current_personality_files_list(data:Identification): check_access(lollmsElfServer, data.client_id) diff --git a/lollms/server/endpoints/lollms_sd.py b/lollms/server/endpoints/lollms_sd.py index 32af892..6acdb5b 100644 --- a/lollms/server/endpoints/lollms_sd.py +++ b/lollms/server/endpoints/lollms_sd.py @@ -14,6 +14,7 @@ from starlette.responses import StreamingResponse from lollms.types import MSG_TYPE from lollms.main_config import BaseConfig from lollms.utilities import detect_antiprompt, remove_text_from_string, trace_exception, find_first_available_file_index, add_period, PackageManager +from lollms.security import check_access from pathlib import Path from ascii_colors import ASCIIColors import os @@ -24,11 +25,19 @@ import platform router = APIRouter() lollmsElfServer:LOLLMSWebUI = LOLLMSWebUI.get_instance() - +class Identification(BaseModel): + client_id: str +class ModelPost(BaseModel): + client_id: str + model_url: str # ----------------------- voice ------------------------------ -@router.get("/install_sd") -def install_sd(): +@router.post("/install_sd") +# async def your_endpoint(request: Request): +# request_data = await request.json() +# print(request_data) # Use proper logging in real applications +def install_sd(data: Identification): + check_access(lollmsElfServer, data.client_id) try: if lollmsElfServer.config.headless_server_mode: return {"status":False,"error":"Service installation is blocked when in headless mode for obvious security reasons!"} @@ -48,8 +57,9 @@ def install_sd(): return {"status":False, 'error':str(ex)} -@router.get("/upgrade_sd") -def upgrade_sd(): +@router.post("/upgrade_sd") +def upgrade_sd(data: Identification): + check_access(lollmsElfServer, data.client_id) try: if lollmsElfServer.config.headless_server_mode: return {"status":False,"error":"Service installation is blocked when in headless mode for obvious security reasons!"} @@ -71,8 +81,9 @@ def upgrade_sd(): -@router.get("/start_sd") -def start_sd(): +@router.post("/start_sd") +def start_sd(data: Identification): + check_access(lollmsElfServer, data.client_id) try: if lollmsElfServer.config.headless_server_mode: return {"status":False,"error":"Service installation is blocked when in headless mode for obvious security reasons!"} @@ -91,8 +102,20 @@ def start_sd(): lollmsElfServer.InfoMessage(f"It looks like I could not install SD because of this error:\n{ex}\nThis is commonly caused by a previous version that I couldn't delete. PLease remove {lollmsElfServer.lollms_paths.personal_path}/shared/auto_sd manually then try again") return {"status":False, 'error':str(ex)} -@router.get("/show_sd") -def show_sd(): +@router.post("/show_sd") +def show_sd(data: Identification): + check_access(lollmsElfServer, data.client_id) import webbrowser webbrowser.open(lollmsElfServer.config.sd_base_url) - return {"status":True} \ No newline at end of file + return {"status":True} + +@router.post("/install_model") +def install_model(data: ModelPost): + check_access(lollmsElfServer, data.client_id) + +@router.get("/sd_is_ready") +def show_sd(): + if hasattr(lollmsElfServer,'sd') and lollmsElfServer.sd is not None: + if lollmsElfServer.sd.ready: + return {"status":True} + return {"status":False} diff --git a/lollms/server/endpoints/lollms_user.py b/lollms/server/endpoints/lollms_user.py index ad0c81a..7266afd 100644 --- a/lollms/server/endpoints/lollms_user.py +++ b/lollms/server/endpoints/lollms_user.py @@ -93,12 +93,12 @@ async def upload_avatar(avatar: UploadFile = File(...)): return {"status": True,"fileName": f"{random_filename}{extension}"} @router.post("/upload_logo") -async def upload_logo(avatar: UploadFile = File(...)): +async def upload_logo(logo: UploadFile = File(...)): """ Uploads a user avatar file to a dedicated directory, preventing path traversal attacks. Parameters: - - avatar: UploadFile object representing the user avatar file. + - logo: UploadFile object representing the user avatar file. Returns: - Dictionary with the status of the upload and the generated file name. @@ -107,19 +107,19 @@ async def upload_logo(avatar: UploadFile = File(...)): - HTTPException with a 400 status code and an error message if the file is invalid or has an invalid type. """ # Only allow certain file types - if avatar.filename.endswith((".jpg", ".png")): + if logo.filename.endswith((".jpg", ".png")): # Create a random file name random_filename = str(uuid.uuid4()) # Use the file extension of the uploaded file - extension = os.path.splitext(avatar.filename)[1] + extension = os.path.splitext(logo.filename)[1] # Create the new file path in a dedicated directory file_location = os.path.join(lollmsElfServer.lollms_paths.personal_user_infos_path, f"{random_filename}{extension}") try: # Open the image to check if it's a valid image - img = Image.open(avatar.file) + img = Image.open(logo.file) # Save the file img.save(file_location) diff --git a/lollms/server/endpoints/lollms_xtts.py b/lollms/server/endpoints/lollms_xtts.py index b6a0df9..ebf8742 100644 --- a/lollms/server/endpoints/lollms_xtts.py +++ b/lollms/server/endpoints/lollms_xtts.py @@ -78,6 +78,89 @@ async def text2Audio(request: LollmsText2AudioRequest): """ Executes Python code and returns the output. + :param request: The HTTP request object. + :return: A JSON response with the status of the operation. + """ + if lollmsElfServer.config.headless_server_mode: + return {"status":False,"error":"Code execution is blocked when in headless mode for obvious security reasons!"} + + if lollmsElfServer.config.host!="localhost" and lollmsElfServer.config.host!="127.0.0.1": + return {"status":False,"error":"Code execution is blocked when the server is exposed outside for very obvious reasons!"} + + if request.fn: + request.fn = os.path.realpath(str((lollmsElfServer.lollms_paths.personal_outputs_path/"audio_out")/request.fn)) + validate_path(request.fn,[str(lollmsElfServer.lollms_paths.personal_outputs_path/"audio_out")]) + + try: + # Get the JSON data from the POST request. + try: + from lollms.services.xtts.lollms_xtts import LollmsXTTS + voice=lollmsElfServer.config.xtts_current_voice + if lollmsElfServer.tts is None: + voice=lollmsElfServer.config.xtts_current_voice + if voice!="main_voice": + voices_folder = lollmsElfServer.lollms_paths.custom_voices_path + else: + voices_folder = Path(__file__).parent.parent.parent/"services/xtts/voices" + + lollmsElfServer.tts = LollmsXTTS( + lollmsElfServer, + voices_folder=voices_folder, + voice_samples_path=Path(__file__).parent/"voices", + xtts_base_url= lollmsElfServer.config.xtts_base_url, + use_deep_speed= lollmsElfServer.config.xtts_use_deep_speed, + use_streaming_mode= lollmsElfServer.config.xtts_use_streaming_mode, + ) + except Exception as ex: + return {"url": None, "error":f"{ex}"} + + voice=lollmsElfServer.config.xtts_current_voice if request.voice is None else request.voice + index = find_first_available_file_index(lollmsElfServer.tts.output_folder, "voice_sample_",".wav") + output_fn=f"voice_sample_{index}.wav" if request.fn is None else request.fn + if voice is None: + voice = "main_voice" + lollmsElfServer.info("Starting to build voice") + try: + from lollms.services.xtts.lollms_xtts import LollmsXTTS + # If the personality has a voice, then use it + if voice!="main_voice": + voices_folder = lollmsElfServer.lollms_paths.custom_voices_path + else: + voices_folder = Path(__file__).parent.parent.parent/"services/xtts/voices" + if lollmsElfServer.tts is None: + lollmsElfServer.tts = LollmsXTTS( + lollmsElfServer, + voices_folder=voices_folder, + voice_samples_path=Path(__file__).parent/"voices", + xtts_base_url= lollmsElfServer.config.xtts_base_url, + use_deep_speed=lollmsElfServer.config.xtts_use_deepspeed, + use_streaming_mode=lollmsElfServer.config.xtts_use_streaming_mode + ) + if lollmsElfServer.tts.ready: + language = lollmsElfServer.config.xtts_current_language# convert_language_name() + lollmsElfServer.tts.set_speaker_folder(voices_folder) + preprocessed_text= add_period(request.text) + voice_file = [v for v in voices_folder.iterdir() if v.stem==voice and v.suffix==".wav"] + if len(voice_file)==0: + return {"status":False,"error":"Voice not found"} + lollmsElfServer.tts.tts_to_audio(preprocessed_text, voice_file[0].name, f"{output_fn}", language=language) + else: + lollmsElfServer.InfoMessage("xtts is not up yet.\nPlease wait for it to load then try again. This may take some time.") + return {"status":False, "error":"Service not ready yet"} + return {"url": url} + except Exception as ex: + trace_exception(ex) + return {"url": None} + except Exception as ex: + trace_exception(ex) + lollmsElfServer.error(ex) + return {"status":False,"error":str(ex)} + +@router.post("/text2wav") +async def text2Wav(request: LollmsText2AudioRequest): + """ + Executes Python code and returns the output. + :param request: The HTTP request object. :return: A JSON response with the status of the operation. """ @@ -143,11 +226,8 @@ async def text2Audio(request: LollmsText2AudioRequest): voice_file = [v for v in voices_folder.iterdir() if v.stem==voice and v.suffix==".wav"] if len(voice_file)==0: return {"status":False,"error":"Voice not found"} - if not lollmsElfServer.config.xtts_use_streaming_mode: - lollmsElfServer.tts.tts_to_file(preprocessed_text, voice_file[0].name, f"{output_fn}", language=language) - lollmsElfServer.info(f"Voice file ready at {url}") - else: - lollmsElfServer.tts.tts_to_audio(preprocessed_text, voice_file[0].name, f"{output_fn}", language=language) + lollmsElfServer.tts.tts_to_file(preprocessed_text, voice_file[0].name, f"{output_fn}", language=language) + lollmsElfServer.info(f"Voice file ready at {url}") else: lollmsElfServer.InfoMessage("xtts is not up yet.\nPlease wait for it to load then try again. This may take some time.") return {"status":False, "error":"Service not ready yet"} @@ -160,6 +240,7 @@ async def text2Audio(request: LollmsText2AudioRequest): lollmsElfServer.error(ex) return {"status":False,"error":str(ex)} + @router.get("/install_xtts") def install_xtts(): try: @@ -211,6 +292,7 @@ async def upload_voice_file(file: UploadFile = File(...)): allowed_extensions = {'wav'} # Use Pathlib to handle the filename + sanitize_path(file.filename) file_path = Path(file.filename) file_extension = file_path.suffix[1:].lower() @@ -231,4 +313,12 @@ async def upload_voice_file(file: UploadFile = File(...)): if lollmsElfServer.config.auto_save: lollmsElfServer.config.save_config() - return {"message": f"Successfully uploaded {safe_filename}"} \ No newline at end of file + return {"message": f"Successfully uploaded {safe_filename}"} + + +@router.get("/xtts_is_ready") +def xtts_is_ready(): + if hasattr(lollmsElfServer,'sd') and lollmsElfServer.sd is not None: + if lollmsElfServer.sd.ready: + return {"status":True} + return {"status":False} diff --git a/lollms/services/sd/lollms_sd.py b/lollms/services/sd/lollms_sd.py index 21d85fd..82aa7e4 100644 --- a/lollms/services/sd/lollms_sd.py +++ b/lollms/services/sd/lollms_sd.py @@ -32,7 +32,7 @@ from lollms.utilities import git_pull, show_yes_no_dialog, run_script_in_env, cr import subprocess import shutil from tqdm import tqdm - +import threading def verify_sd(lollms_paths:LollmsPaths): # Clone repository @@ -57,24 +57,34 @@ def download_file(url, folder_path, local_filename): return local_filename +def install_model(lollms_app:LollmsApplication, model_url): + root_dir = lollms_app.lollms_paths.personal_path + shared_folder = root_dir/"shared" + sd_folder = shared_folder / "auto_sd" + download_file(model_url, sd_folder/"models/Stable-diffusion","Juggernaut-XL_v9_RunDiffusionPhoto_v2.safetensors") + def install_sd(lollms_app:LollmsApplication): root_dir = lollms_app.lollms_paths.personal_path shared_folder = root_dir/"shared" sd_folder = shared_folder / "auto_sd" - if sd_folder.exists(): - if show_yes_no_dialog("warning!","I have detected that there is a previous installation of stable diffusion.\nShould I remove it and continue installing?"): - shutil.rmtree(sd_folder) - elif not show_yes_no_dialog("warning!","Continue installation?"): - return - ASCIIColors.cyan("Installing autosd conda environment with python 3.10") create_conda_env("autosd","3.10") ASCIIColors.cyan("Done") + if os.path.exists(str(sd_folder)): + print("Repository already exists. Pulling latest changes...") + try: + subprocess.run(["git", "-C", str(sd_folder), "pull"], check=True) + subprocess.run(["git", "-C", str(sd_folder/"extensions/SD-CN-Animation"), "pull"]) + except: + subprocess.run(["git", "clone", "https://github.com/ParisNeo/stable-diffusion-webui.git", str(sd_folder)]) + else: + print("Cloning repository...") + subprocess.run(["git", "clone", "https://github.com/ParisNeo/stable-diffusion-webui.git", str(sd_folder)]) + subprocess.run(["git", "clone", "https://github.com/deforum-art/sd-webui-deforum.git", str(sd_folder/"extensions/sd-webui-deforum")]) + subprocess.run(["git", "clone", "https://github.com/ParisNeo/SD-CN-Animation.git", str(sd_folder/"extensions/SD-CN-Animation")]) - subprocess.run(["git", "clone", "https://github.com/ParisNeo/stable-diffusion-webui.git", str(sd_folder)]) - subprocess.run(["git", "clone", "https://github.com/ParisNeo/SD-CN-Animation.git", str(sd_folder/"extensions/SD-CN-Animation")]) - if show_yes_no_dialog("warning!","Do you want to install a model from civitai?\nI suggest dreamshaper xl.\nYou can install models manually by putting them inside your persona folder/auto_sd/models/stable_diffusion"): - download_file("https://civitai.com/api/download/models/351306", sd_folder/"models/Stable-diffusion","dreamshaperXL_v21TurboDPMSDE.safetensors") + if show_yes_no_dialog("warning!","Do you want to install a model?\nI suggest Juggernaut-XL_v9.\nYou can install models manually by putting them inside your persona folder/auto_sd/models/stable_diffusion"): + download_file("https://huggingface.co/RunDiffusion/Juggernaut-XL-v9/resolve/main/Juggernaut-XL_v9_RunDiffusionPhoto_v2.safetensors", sd_folder/"models/Stable-diffusion","Juggernaut-XL_v9_RunDiffusionPhoto_v2.safetensors") lollms_app.sd = LollmsSD(lollms_app) ASCIIColors.green("Stable diffusion installed successfully") @@ -84,15 +94,29 @@ def upgrade_sd(lollms_app:LollmsApplication): root_dir = lollms_app.lollms_paths.personal_path shared_folder = root_dir/"shared" sd_folder = shared_folder / "auto_sd" - if sd_folder.exists(): - if show_yes_no_dialog("warning!","I have detected that there is a previous installation of stable diffusion.\nShould I remove it and continue installing?"): - shutil.rmtree(sd_folder) - elif not show_yes_no_dialog("warning!","Continue installation?"): - return + if os.path.exists(str(sd_folder)): + print("Repository already exists. Pulling latest changes...") + try: + subprocess.run(["git", "-C", str(sd_folder), "pull"], check=True) + except: + subprocess.run(["git", "clone", "https://github.com/ParisNeo/stable-diffusion-webui.git", str(sd_folder)]) + + try: + subprocess.run(["git", "-C", str(sd_folder/"extensions/sd-webui-deforum"), "pull"]) + except: + subprocess.run(["git", "clone", "https://github.com/deforum-art/sd-webui-deforum.git", str(sd_folder/"extensions/sd-webui-deforum")]) - subprocess.run(["git", "clone", "https://github.com/ParisNeo/stable-diffusion-webui.git", str(sd_folder)]) - subprocess.run(["git", "clone", "https://github.com/ParisNeo/SD-CN-Animation.git", str(sd_folder/"extensions/SD-CN-Animation")]) + try: + subprocess.run(["git", "-C", str(sd_folder/"extensions/SD-CN-Animation"), "pull"]) + except: + subprocess.run(["git", "clone", "https://github.com/ParisNeo/SD-CN-Animation.git", str(sd_folder/"extensions/SD-CN-Animation")]) + else: + print("Cloning repository...") + subprocess.run(["git", "clone", "https://github.com/ParisNeo/stable-diffusion-webui.git", str(sd_folder)]) + subprocess.run(["git", "clone", "https://github.com/deforum-art/sd-webui-deforum.git", str(sd_folder/"extensions/sd-webui-deforum")]) + subprocess.run(["git", "clone", "https://github.com/ParisNeo/SD-CN-Animation.git", str(sd_folder/"extensions/SD-CN-Animation")]) + def upgrade_sd(lollms_app:LollmsApplication): @@ -263,6 +287,7 @@ class LollmsSD: ): if auto_sd_base_url=="" or auto_sd_base_url=="http://127.0.0.1:7860": auto_sd_base_url = None + self.ready = False # Get the current directory lollms_paths = app.lollms_paths self.app = app @@ -322,7 +347,7 @@ class LollmsSD: if wait_for_service: self.wait_for_service(max_retries=max_retries) else: - ASCIIColors.warning("We are not waiting for the SD service to be up.\nThis means that you may need to wait a bit before you can use it.") + self.wait_for_service_in_another_thread(max_retries=max_retries) self.default_sampler = sampler self.default_steps = steps @@ -1053,7 +1078,10 @@ class LollmsSD: time.sleep(check_interval) - + def wait_for_service_in_another_thread(self, max_retries=150, show_warning=True): + thread = threading.Thread(target=self.wait_for_service, args=(max_retries, show_warning)) + thread.start() + return thread def wait_for_service(self, max_retries = 50, show_warning=True): url = f"{self.auto_sd_base_url}/internal/ping" @@ -1067,6 +1095,7 @@ class LollmsSD: print("Service is available.") if self.app is not None: self.app.success("SD Service is now available.") + self.ready = True return True except requests.exceptions.RequestException: pass diff --git a/lollms/services/xtts/lollms_xtts.py b/lollms/services/xtts/lollms_xtts.py index 92aaa4c..0ffe6ae 100644 --- a/lollms/services/xtts/lollms_xtts.py +++ b/lollms/services/xtts/lollms_xtts.py @@ -27,6 +27,7 @@ from dataclasses import dataclass from PIL import Image, PngImagePlugin from enum import Enum from typing import List, Dict, Any +import uuid from ascii_colors import ASCIIColors, trace_exception from lollms.paths import LollmsPaths @@ -116,6 +117,7 @@ class LollmsXTTS: use_deep_speed=False, use_streaming_mode = True ): + self.generation_threads = [] self.voices_folder = voices_folder self.ready = False if xtts_base_url=="" or xtts_base_url=="http://127.0.0.1:8020": @@ -194,7 +196,7 @@ class LollmsXTTS: if response.status_code == 200: print(f"voices_folder is {self.voices_folder}.") if self.voices_folder is not None: - print("Senerating sample audio.") + print("Generating sample audio.") voice_file = [v for v in self.voices_folder.iterdir() if v.suffix==".wav"] self.tts_to_audio("xtts is ready",voice_file[0].name) print("Service is available.") @@ -260,31 +262,49 @@ class LollmsXTTS: else: print("Request failed with status code:", response.status_code) - def tts_to_audio(self, text, speaker_wav, file_name_or_path:Path|str=None, language="en"): - url = f"{self.xtts_base_url}/tts_to_audio" + def tts_to_audio(self, text, speaker_wav, file_name_or_path:Path|str=None, language="en", use_threading=False): + def tts2_audio_th(thread_uid=None): + url = f"{self.xtts_base_url}/tts_to_audio" - # Define the request body - payload = { - "text": text, - "speaker_wav": speaker_wav, - "language": language - } - headers = { - 'accept': 'application/json', - 'Content-Type': 'application/json' - } + # Define the request body + payload = { + "text": text, + "speaker_wav": speaker_wav, + "language": language + } + headers = { + 'accept': 'application/json', + 'Content-Type': 'application/json' + } - # Send the POST request - response = requests.post(url, headers=headers, data=json.dumps(payload)) + # Send the POST request + response = requests.post(url, headers=headers, data=json.dumps(payload)) - # Check the response status code - if response.status_code == 200: - print("Request successful") - # You can access the response data using response.json() - # Open a new file in binary write mode - if file_name_or_path is not None: - with open(self.output_folder/file_name_or_path, 'wb') as file: - # Write the binary content to the file - file.write(response.content) + if response.status_code == 200: + print("Request successful") + print("Response headers:", response.headers) + + # Basic logging for debugging + print("First 100 bytes of response content:", response.content[:100]) + + if file_name_or_path is not None: + try: + with open(self.output_folder / file_name_or_path, 'wb') as file: + # Write the binary content to the file + file.write(response.content) + print(f"File {file_name_or_path} written successfully.") + except Exception as e: + print(f"Failed to write the file. Error: {e}") + else: + print("Request failed with status code:", response.status_code) + if thread_uid: + self.generation_threads.pop(thread_uid, None) + if use_threading: + thread_uid = str(uuid.uuid4()) + thread = threading.Thread(target=tts2_audio_th, args=(thread_uid)) + self.generation_threads[thread_uid]=thread + thread.start() + ASCIIColors.green("Generation started") + return thread else: - print("Request failed with status code:", response.status_code) \ No newline at end of file + return tts2_audio_th() diff --git a/personal_data/configs/lollms_discord_local_config.yaml b/personal_data/configs/lollms_discord_local_config.yaml index 4e2af3f..a52c570 100644 --- a/personal_data/configs/lollms_discord_local_config.yaml +++ b/personal_data/configs/lollms_discord_local_config.yaml @@ -78,7 +78,7 @@ auto_show_browser: true copy_to_clipboard_add_all_details: false # Voice service -enable_voice_service: false +xtts_enable: false xtts_base_url: http://localhost:8020 auto_read: false xtts_current_voice: null