From 72abc43db8f732743c65d6f6a820369ab8665766 Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 4 Feb 2026 16:19:22 +0100 Subject: more asynchronous work and other minor improvements --- cogs/tts.py | 138 +++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 80 insertions(+), 58 deletions(-) (limited to 'cogs/tts.py') diff --git a/cogs/tts.py b/cogs/tts.py index 3c2f9ce..158006c 100644 --- a/cogs/tts.py +++ b/cogs/tts.py @@ -6,7 +6,7 @@ import os import json import discord -import tempfile +import aiofiles import logging from discord.ext import commands from gtts import gTTS @@ -18,80 +18,102 @@ class TTS(commands.Cog): def __init__(self, bot): self.bot = bot + async def _load_settings(self): + """Load volume settings from settings.json""" + volume = 1.0 + if os.path.exists('settings.json'): + try: + async with aiofiles.open('settings.json') as f: + data = json.load(f) + volume = data.get('volume', 1.0) + except (json.JSONDecodeError, KeyError) as e: + logger.error(f"Error loading settings: {e}") + return volume + + async def _handle_file_output(self, ctx, temp_path, text): + """Handle TTS file output""" + try: + await ctx.respond(file=discord.File(temp_path, filename='tts.mp3')) + logger.info(f"Sent TTS for text: {text}") + except Exception as e: + logger.error(f"Error sending file: {e}") + await ctx.respond("eror :c", ephemeral=True) + + async def _get_or_connect_voice_client(self, channel): + """Get existing voice client or connect to new channel""" + voice_client = discord.utils.get(self.bot.voice_clients, guild=channel.guild) + + if not voice_client or not voice_client.is_connected(): + voice_client = await channel.connect() + elif voice_client.channel != channel: + await voice_client.move_to(channel) + + return voice_client + + async def _handle_voice_output(self, ctx, temp_path, text, volume): + """Handle TTS voice channel output""" + await ctx.respond(content="\u200b", ephemeral=True) + await ctx.interaction.delete_original_response() + + # Check if the user is in a voice channel + if not (ctx.author.voice and ctx.author.voice.channel): + logger.info("User is not in a voice channel") + return + + channel = ctx.author.voice.channel + try: + voice_client = await self._get_or_connect_voice_client(channel) + + # Play the TTS audio with volume adjustment + audio_source = discord.FFmpegPCMAudio( + temp_path, + options=f'-filter:a "volume={volume}"' + ) + voice_client.play(audio_source) + logger.info(f"Playing TTS: {text}") + + # Wait for the audio to finish playing + while voice_client.is_playing(): + await asyncio.sleep(0.1) + + except Exception as e: + logger.error(f"Error in voice channel handling: {e}") + await ctx.respond("eror :c", ephemeral=True) + + async def _cleanup_temp_file(self, temp_path): + """Clean up temporary file""" + try: + if temp_path and os.path.exists(temp_path): + os.unlink(temp_path) + except Exception as e: + logger.error(f"Error cleaning up temp file {temp_path}: {e}") + @commands.slash_command(name="tts", description="Generuje TTS dla danego tekstu") async def tts(self, ctx, text: str = discord.Option(str, "Tekst do przeczytania", required=True), output: str = discord.Option(str, "Wybierz sposób odtwarzania", choices=["file", "vc"], default="vc") ): await ctx.defer() - # Create a temporary file for TTS output - with tempfile.NamedTemporaryFile(suffix='.mp3', delete=False) as temp_file: - temp_path = temp_file.name + async with NamedTemporaryFile(suffix=".mp3", delete=False) as temp_file: + temp_path = temp_file.name + try: # Generate and save TTS to temp file tts = gTTS(text=text, lang='pl') tts.save(temp_path) - # Load settings if available - volume = 1.0 # Default volume - if os.path.exists('settings.json'): - try: - with open('settings.json') as f: - data = json.load(f) - volume = data.get('volume', 1.0) - except (json.JSONDecodeError, KeyError) as e: - logger.error(f"Error loading settings: {e}") + # Load settings + volume = await self._load_settings() + # Handle output based on user choice if output == "file": - try: - await ctx.respond(file=discord.File(temp_path, filename='tts.mp3')) - logger.info(f"Sent TTS for text: {text}") - except Exception as e: - logger.error(f"Error sending file: {e}") - await ctx.respond("eror :c", ephemeral=True) - + await self._handle_file_output(ctx, temp_path, text) elif output == "vc": - await ctx.respond(content="\u200b", ephemeral=True) - await ctx.interaction.delete_original_response() - - # Check if the user is in a voice channel - if not (ctx.author.voice and ctx.author.voice.channel): - logger.info("User is not in a voice channel") - return - - channel = ctx.author.voice.channel - try: - # Connect to the voice channel if not already connected - voice_client = discord.utils.get(self.bot.voice_clients, guild=channel.guild) - if not voice_client or not voice_client.is_connected(): - voice_client = await channel.connect() - elif voice_client.channel != channel: - await voice_client.move_to(channel) - - # Play the TTS audio with volume adjustment - audio_source = discord.FFmpegPCMAudio( - temp_path, - options=f'-filter:a "volume={volume}"' - ) - voice_client.play(audio_source) - logger.info(f"Playing TTS: {text}") - - # Wait for the audio to finish playing - while voice_client.is_playing(): - await asyncio.sleep(0.1) - - except Exception as e: - logger.error(f"Error in voice channel handling: {e}") - await ctx.respond("eror :c", ephemeral=True) + await self._handle_voice_output(ctx, temp_path, text, volume) finally: - # Clean up the temporary file - try: - if temp_path and os.path.exists(temp_path): - os.unlink(temp_path) - except Exception as e: - logger.error(f"Error cleaning up temp file {temp_path}: {e}") + await self._cleanup_temp_file(temp_path) def setup(bot): bot.add_cog(TTS(bot)) \ No newline at end of file -- cgit v1.2.3