[Python] wrapping text in obs with python

Discussão em 'Python' iniciado por Stack, Fevereiro 6, 2025.

  1. Stack

    Stack Membro Participativo

    i am building a live translator for Twitch that uses OBS. i am trying to change the size of a text element in OBS using python depending on the size of the text itself, so that the text never goes off the 16x9 border, but i cant seem to figure it out. I don't get any particular error, it just does not change the size of the element or font size.

    import time
    import speech_recognition as sr
    from googletrans import Translator
    from obsws_python import ReqClient # Use ReqClient for OBS WebSocket 5.x
    from twitchio.ext import commands # For Twitch chat integration
    import threading
    import tkinter as tk
    from tkinter import messagebox

    # Global variable to control translation state
    translation_enabled = False
    src_lang = 'fr'
    dest_lang = 'en'


    # Function to recognize speech from microphone input
    def recognize_speech_from_mic(recognizer, microphone):
    with microphone as source:
    recognizer.adjust_for_ambient_noise(source)
    audio = recognizer.listen(source)

    response = {
    "success": True,
    "error": None,
    "transcription": None
    }

    try:
    response["transcription"] = recognizer.recognize_google(audio, language=src_lang)
    except sr.RequestError:
    response["success"] = False
    response["error"] = "API unavailable"
    except sr.UnknownValueError:
    response["error"] = "Unable to recognize speech"

    return response


    # Function to translate text using Google Translate
    def translate_text(text, src_lang='fr', dest_lang='en'):
    translator = Translator()
    translation = translator.translate(text, src=src_lang, dest=dest_lang)
    return translation.text


    # Function to get the transform properties of a source in OBS
    def get_source_transform(client, scene_name, source_name):
    # Get the scene item ID
    response = client.get_scene_item_id(scene_name=scene_name, source_name=source_name)
    myItemID = response.scene_item_id

    # Get the transform properties of the scene item
    response = client.get_scene_item_transform(scene_name=scene_name, item_id=myItemID)
    transform = {
    "positionX": response.scene_item_transform.positionX,
    "positionY": response.scene_item_transform.positionY,
    "scaleX": response.scene_item_transform.scaleX,
    "scaleY": response.scene_item_transform.scaleY,
    "rotation": response.scene_item_transform.rotation,
    "width": response.scene_item_transform.width,
    "height": response.scene_item_transform.height,
    "sourceWidth": response.scene_item_transform.sourceWidth,
    "sourceHeight": response.scene_item_transform.sourceHeight,
    "cropLeft": response.scene_item_transform.cropLeft,
    "cropRight": response.scene_item_transform.cropRight,
    "cropTop": response.scene_item_transform.cropTop,
    "cropBottom": response.scene_item_transform.cropBottom,
    }
    return transform


    # Function to set the transform properties of a source in OBS
    def set_source_transform(client, scene_name, source_name, new_transform):
    # Get the scene item ID
    response = client.get_scene_item_id(scene_name=scene_name, source_name=source_name)
    myItemID = response.scene_item_id

    # Set the transform properties of the scene item
    client.set_scene_item_transform(scene_name=scene_name, item_id=myItemID, scene_item_transform=new_transform)


    # Function to calculate the required scale for the text to fit within the screen
    def calculate_scale(text, max_width=1920, max_height=1080):
    """
    Calculate the required scale for the text to fit within the screen.
    """
    base_font_size = 50 # Starting font size
    avg_char_width = 20 # Average width of a character in pixels for font size 50

    # Calculate the required width based on text length
    required_width = len(text) * avg_char_width

    # Calculate the required scale to fit within the screen width
    if required_width > max_width:
    scale = max_width / required_width
    else:
    scale = 1.0

    # Ensure the scale doesn't go too small
    scale = max(scale, 0.5)

    return scale


    # Function to display text one word at a time with dynamic scaling
    def display_text_word_by_word(client, text, scene_name="Scene", source_name="TranslatedText", delay=0.5):
    words = text.split() # Split the text into words
    for word in words:
    if not translation_enabled:
    break # Stop if translation is disabled

    # Calculate the required scale for the current word
    scale = calculate_scale(word)

    # Update the text in OBS
    client.set_input_settings(
    source_name,
    {"text": word},
    overlay=True
    )

    # Adjust the scale of the text source
    transform = get_source_transform(client, scene_name, source_name)
    transform["scaleX"] = scale
    transform["scaleY"] = scale
    set_source_transform(client, scene_name, source_name, transform)

    time.sleep(delay) # Delay between words


    # Function to connect to OBS WebSocket
    def connect_to_obs(host='localhost', port=4455, password='TwitchChat9'):
    try:
    print(f"Attempting to connect to OBS WebSocket at {host}:{port} with password: {password}")
    client = ReqClient(host=host, port=port, password=password)
    print("Connected to OBS WebSocket server!")
    return client
    except Exception as e:
    print(f"Failed to connect to OBS WebSocket: {e}")
    return None

    # Retry logic for OBS WebSocket connection
    def connect_to_obs_with_retry(host='localhost', port=4455, password='TwitchChat9', retries=5, delay=5):
    for attempt in range(retries):
    client = connect_to_obs(host, port, password)
    if client:
    return client
    else:
    print(f"Retry {attempt + 1}/{retries} failed, retrying in {delay} seconds...")
    time.sleep(delay)
    return None


    # Function to handle live translation
    def live_translation(obs_client):
    recognizer = sr.Recognizer()
    microphone = sr.Microphone()

    while True:
    if translation_enabled:
    print("Listening...")
    # Recognize speech from the microphone
    speech_result = recognize_speech_from_mic(recognizer, microphone)

    if speech_result["success"] and speech_result["transcription"]:
    # Get the recognized text
    text = speech_result["transcription"]
    print(f"Recognized: {text}")

    # Translate the text
    translated_text = translate_text(text, src_lang, dest_lang)
    print(f"Translated: {translated_text}")

    # Display the translated text one word at a time
    display_text_word_by_word(obs_client, translated_text)

    # Non-blocking loop
    time.sleep(0.5)


    # Twitch bot class
    class TwitchBot(commands.Bot):
    def __init__(self, oauth_token, twitch_username):
    super().__init__(
    token=oauth_token,
    prefix="!",
    initial_channels=[twitch_username]
    )
    self.oauth_token = oauth_token
    self.twitch_username = twitch_username

    async def event_ready(self):
    print(f"Logged in as {self.nick}")

    @commands.command(name="translate")
    async def translate_command(self, ctx):
    global translation_enabled
    translation_enabled = not translation_enabled # Toggle translation state
    status = "enabled" if translation_enabled else "disabled"
    await ctx.send(f"Live translation is now {status}.")


    # Function to create the GUI for user input
    def create_gui():
    def start_program():
    oauth_token = oauth_token_entry.get()
    twitch_username = twitch_username_entry.get()
    obs_password = obs_password_entry.get()

    if not oauth_token or not twitch_username or not obs_password:
    messagebox.showerror("Error", "All fields are required!")
    return

    # Try connecting to OBS
    obs_client = connect_to_obs_with_retry(password=obs_password)
    if not obs_client:
    messagebox.showerror("Error", "Failed to connect to OBS WebSocket after multiple attempts.")
    return

    # Start translation thread
    translation_thread = threading.Thread(target=live_translation, args=(obs_client,))
    translation_thread.daemon = True
    translation_thread.start()

    # Start the Twitch bot
    bot = TwitchBot(oauth_token, twitch_username)
    bot.run() # This should be called after everything else is ready

    # GUI Setup
    window = tk.Tk()
    window.title("Translation Settings")

    # Create GUI components
    tk.Label(window, text="OAuth Token:").grid(row=0, column=0)
    oauth_token_entry = tk.Entry(window, width=30)
    oauth_token_entry.grid(row=0, column=1)

    tk.Label(window, text="Twitch Username:").grid(row=1, column=0)
    twitch_username_entry = tk.Entry(window, width=30)
    twitch_username_entry.grid(row=1, column=1)

    tk.Label(window, text="OBS WebSocket Password:").grid(row=2, column=0)
    obs_password_entry = tk.Entry(window, width=30)
    obs_password_entry.grid(row=2, column=1)

    # Start button
    start_button = tk.Button(window, text="Start Program", command=start_program)
    start_button.grid(row=3, columnspan=2)

    # Language selection (for future feature)
    def update_languages():
    global src_lang, dest_lang
    src_lang = src_lang_var.get()
    dest_lang = dest_lang_var.get()

    tk.Label(window, text="Source Language:").grid(row=4, column=0)
    src_lang_var = tk.StringVar(window)
    src_lang_var.set("fr") # Default source language
    src_lang_menu = tk.OptionMenu(window, src_lang_var, "fr", "en", "es", "de", "ja", "ko", "zh")
    src_lang_menu.grid(row=4, column=1)

    tk.Label(window, text="Destination Language:").grid(row=5, column=0)
    dest_lang_var = tk.StringVar(window)
    dest_lang_var.set("en") # Default destination language
    dest_lang_menu = tk.OptionMenu(window, dest_lang_var, "en", "fr", "es", "de", "ja", "ko","zh")
    dest_lang_menu.grid(row=5, column=1)

    update_button = tk.Button(window, text="Update Languages", command=update_languages)
    update_button.grid(row=6, columnspan=2)

    window.mainloop()


    # Main function to run the program
    def main():
    create_gui()


    # Run the program
    if __name__ == "__main__":
    main()

    Continue reading...

Compartilhe esta Página