From ab9c0502c362c941095d2dafa5a90ab7b780406e Mon Sep 17 00:00:00 2001 From: Oleksandr K <148587026+devman0129@users.noreply.github.com> Date: Wed, 8 May 2024 18:22:07 +0300 Subject: [PATCH] Add python example for dubbing a video (#25) * fix: Handle by pre-commit * fix: Do ruff fix --- examples/dubbing/python/create_a_dub.py | 82 ------------------- .../dubbing/python/create_a_dub_from_file.py | 71 ++++++++++++++++ .../dubbing/python/create_a_dub_from_url.py | 64 +++++++++++++++ examples/dubbing/python/dubbing_utils.py | 72 ++++++++++++++++ 4 files changed, 207 insertions(+), 82 deletions(-) delete mode 100644 examples/dubbing/python/create_a_dub.py create mode 100644 examples/dubbing/python/create_a_dub_from_file.py create mode 100644 examples/dubbing/python/create_a_dub_from_url.py create mode 100644 examples/dubbing/python/dubbing_utils.py diff --git a/examples/dubbing/python/create_a_dub.py b/examples/dubbing/python/create_a_dub.py deleted file mode 100644 index 46aa54b..0000000 --- a/examples/dubbing/python/create_a_dub.py +++ /dev/null @@ -1,82 +0,0 @@ -import os -import time - -import requests -from dotenv import load_dotenv -from elevenlabs.client import ElevenLabs - -load_dotenv() - -ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY") - -if ELEVENLABS_API_KEY is None: - raise ValueError( - "ELEVENLABS_API_KEY environment variable not found, please copy the .env.example file to .env and fill in the API key" - ) - -client = ElevenLabs(api_key=ELEVENLABS_API_KEY) - - -def create_dub( - file_name, format, input_file_path, output_file_path, source_lang, target_language -): - with open(input_file_path, "rb") as audio_file: - response = client.dubbing.dub_a_video_or_an_audio_file( - file=(file_name, audio_file, format), - target_lang=target_language, - mode="automatic", - source_lang="en", - num_speakers=1, - watermark=True, # reduces the characters used - ) - - dubbing_id = response.dubbing_id - - for i in range(1000): - headers = { - "xi-api-key": ELEVENLABS_API_KEY, - } - - metadata = client.dubbing.get_dubbing_project_metadata(dubbing_id=dubbing_id) - if metadata.status == "dubbed": - # TODO: fix the response type of client.dubbing.get_dubbed_file - response = requests.get( - "https://api.elevenlabs.io/v1/dubbing/" - + dubbing_id - + "/audio/" - + target_language, - headers=headers, - ) - - if response.status_code == 200: - with open(output_file_path, "wb") as file: - file.write(response.content) - print("Dubbing complete and saved to dubbed_file.mp4") - - return - - elif metadata.status == "dubbing": - print("Dubbing in progress... Will check status again in 10 seconds") - time.sleep(10) - else: - print("Dubbing failed", response.json()) - return - - if i == 10: - print("Dubbing timed out") - - -if __name__ == "__main__": - input_file_path = os.path.join(os.path.dirname(__file__), "../example_speech.mp3") - output_file_path = "dubbed_file.mp4" - - source_language = "en" - target_language = "es" - create_dub( - "example_speech.mp3", - "audio/mpeg", - input_file_path, - output_file_path, - source_language, - target_language, - ) diff --git a/examples/dubbing/python/create_a_dub_from_file.py b/examples/dubbing/python/create_a_dub_from_file.py new file mode 100644 index 0000000..2c90376 --- /dev/null +++ b/examples/dubbing/python/create_a_dub_from_file.py @@ -0,0 +1,71 @@ +import os +from typing import Optional + +from dotenv import load_dotenv +from dubbing_utils import download_dubbed_file, wait_for_dubbing_completion +from elevenlabs.client import ElevenLabs + +# Load environment variables +load_dotenv() + +# Retrieve the API key +ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY") +if not ELEVENLABS_API_KEY: + raise ValueError( + "ELEVENLABS_API_KEY environment variable not found. " + "Please set the API key in your environment variables." + ) + +client = ElevenLabs(api_key=ELEVENLABS_API_KEY) + + +def create_dub_from_file( + input_file_path: str, + file_format: str, + source_language: str, + target_language: str, +) -> Optional[str]: + """ + Dubs an audio or video file from one language to another and saves the output. + + Args: + input_file_path (str): The file path of the audio or video to dub. + file_format (str): The file format of the input file. + source_language (str): The language of the input file. + target_language (str): The target language to dub into. + + Returns: + Optional[str]: The file path of the dubbed file or None if operation failed. + """ + if not os.path.isfile(input_file_path): + raise FileNotFoundError(f"The input file does not exist: {input_file_path}") + + with open(input_file_path, "rb") as audio_file: + response = client.dubbing.dub_a_video_or_an_audio_file( + file=(os.path.basename(input_file_path), audio_file, file_format), + target_lang=target_language, + mode="automatic", + source_lang=source_language, + num_speakers=1, + watermark=False, # reduces the characters used if enabled, only works for videos not audio + ) + + dubbing_id = response.dubbing_id + if wait_for_dubbing_completion(dubbing_id): + output_file_path = download_dubbed_file(dubbing_id, target_language) + return output_file_path + else: + return None + + +if __name__ == "__main__": + result = create_dub_from_file( + "../example_speech.mp3", # Input file path + "audio/mpeg", # File format + "en", # Source language + "es", # Target language + ) + if result: + print("Dubbing was successful! File saved at:", result) + else: + print("Dubbing failed or timed out.") diff --git a/examples/dubbing/python/create_a_dub_from_url.py b/examples/dubbing/python/create_a_dub_from_url.py new file mode 100644 index 0000000..a4c4213 --- /dev/null +++ b/examples/dubbing/python/create_a_dub_from_url.py @@ -0,0 +1,64 @@ +import os +from typing import Optional + +from dotenv import load_dotenv +from dubbing_utils import download_dubbed_file, wait_for_dubbing_completion +from elevenlabs.client import ElevenLabs + +# Load environment variables +load_dotenv() + +# Retrieve the API key +ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY") +if not ELEVENLABS_API_KEY: + raise ValueError( + "ELEVENLABS_API_KEY environment variable not found. " + "Please set the API key in your environment variables." + ) + +client = ElevenLabs(api_key=ELEVENLABS_API_KEY) + + +def create_dub_from_url( + source_url: str, + source_language: str, + target_language: str, +) -> Optional[str]: + """ + Downloads a video from a URL, and creates a dubbed version in the target language. + + Args: + source_url (str): The URL of the source video to dub. Can be a YouTube link, TikTok, X (Twitter) or a Vimeo link. + source_language (str): The language of the source video. + target_language (str): The target language to dub into. + + Returns: + Optional[str]: The file path of the dubbed file or None if operation failed. + """ + + response = client.dubbing.dub_a_video_or_an_audio_file( + source_url=source_url, + target_lang=target_language, + mode="automatic", + source_lang=source_language, + num_speakers=1, + watermark=True, # reduces the characters used + ) + + dubbing_id = response.dubbing_id + if wait_for_dubbing_completion(dubbing_id): + output_file_path = download_dubbed_file(dubbing_id, target_language) + return output_file_path + else: + return None + + +if __name__ == "__main__": + source_url = "https://www.youtube.com/watch?v=0EqSXDwTq6U" # Charlie bit my finger + source_language = "en" + target_language = "fr" + result = create_dub_from_url(source_url, source_language, target_language) + if result: + print("Dubbing was successful! File saved at:", result) + else: + print("Dubbing failed or timed out.") diff --git a/examples/dubbing/python/dubbing_utils.py b/examples/dubbing/python/dubbing_utils.py new file mode 100644 index 0000000..ab3e28c --- /dev/null +++ b/examples/dubbing/python/dubbing_utils.py @@ -0,0 +1,72 @@ +import os +import time + +from dotenv import load_dotenv +from elevenlabs.client import ElevenLabs + +# Load environment variables +load_dotenv() + +# Retrieve the API key +ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY") +if not ELEVENLABS_API_KEY: + raise ValueError( + "ELEVENLABS_API_KEY environment variable not found. " + "Please set the API key in your environment variables." + ) + +client = ElevenLabs(api_key=ELEVENLABS_API_KEY) + + +def download_dubbed_file(dubbing_id: str, language_code: str) -> str: + """ + Downloads the dubbed file for a given dubbing ID and language code. + + Args: + dubbing_id: The ID of the dubbing project. + language_code: The language code for the dubbing. + + Returns: + The file path to the downloaded dubbed file. + """ + dir_path = f"data/{dubbing_id}" + os.makedirs(dir_path, exist_ok=True) + + file_path = f"{dir_path}/{language_code}.mp4" + with open(file_path, "wb") as file: + for chunk in client.dubbing.get_dubbed_file(dubbing_id, language_code): + file.write(chunk) + + return file_path + + +def wait_for_dubbing_completion(dubbing_id: str) -> bool: + """ + Waits for the dubbing process to complete by periodically checking the status. + + Args: + dubbing_id (str): The dubbing project id. + + Returns: + bool: True if the dubbing is successful, False otherwise. + """ + MAX_ATTEMPTS = 120 + CHECK_INTERVAL = 10 # In seconds + + for _ in range(MAX_ATTEMPTS): + metadata = client.dubbing.get_dubbing_project_metadata(dubbing_id) + if metadata.status == "dubbed": + return True + elif metadata.status == "dubbing": + print( + "Dubbing in progress... Will check status again in", + CHECK_INTERVAL, + "seconds.", + ) + time.sleep(CHECK_INTERVAL) + else: + print("Dubbing failed:", metadata.error_message) + return False + + print("Dubbing timed out") + return False