From b33c4729d3b12fdbbb3f675ca88b44925debd9ae Mon Sep 17 00:00:00 2001 From: salmonik Date: Sat, 3 Jan 2026 20:43:11 +0100 Subject: fixed compressed play --- cogs/music/play.py | 77 +++++++++++++++++++++++++++++++++++++++++++----- cogs/music/play_utils.py | 60 ++++++++++++++++++++++++++++++------- 2 files changed, 120 insertions(+), 17 deletions(-) (limited to 'cogs') diff --git a/cogs/music/play.py b/cogs/music/play.py index 74b9e4f..8390ff5 100644 --- a/cogs/music/play.py +++ b/cogs/music/play.py @@ -48,15 +48,78 @@ class Play(commands.Cog): else: await ctx.respond("Nie znaleziono pliku do odtworzenia", ephemeral=True) + @discord.slash_command(name="grajcuj_zkompresowane", description="grajcujmy wszyscy w wysokiej jakosci!") + async def play_compressed( + self, + ctx: discord.ApplicationContext, + query: str = discord.Option(str, "co zagrać??", required=True), + source: str = discord.Option(str, "skąd zagrać??", required=True, + choices=["local", "soundcloud", "youtube"]) + ): + await ctx.defer(ephemeral=True) + if source == "local": + candidates = await play_utils.PlayUtils(self.bot).search_local(query) + if candidates: + embed = discord.Embed(title="Wyniki wyszukiwania", description="Wybierz plik do odtworzenia:", + color=discord.Color.blue()) + view = discord.ui.View(disable_on_timeout=True) + for i, candidate in enumerate(candidates, start=1): + filename = os.path.basename(candidate) + metadata = None + if os.path.splitext(filename)[1].lower() in ['.mp3', '.wav', '.opus']: + metadata = mutagen.File(candidate) + title = metadata.get('title', None) + if metadata and title: + embed.add_field(name=f"{i}. {title}", value="\u200b", inline=False) + else: + embed.add_field(name=f"{i}. {filename}", value="\u200b", inline=False) + button = discord.ui.Button(label=f"Play {i}") + async def button_callback(interaction: discord.Interaction, candidate=candidate): + await interaction.response.defer(ephemeral=True) + try: + await ctx.interaction.delete_original_response() + # Compress the file before playing + compressed_path = await play_utils.PlayUtils(self.bot).compress(ctx, candidate) + if compressed_path: + try: + await play_utils.PlayUtils(self.bot).play(ctx, compressed_path, delete_after_play=True) + except Exception as e: + # Clean up the compressed file if play fails + if os.path.exists(compressed_path): + try: + os.remove(compressed_path) + logger.info(f"Cleaned up temporary file after play error: {compressed_path}") + except Exception as cleanup_error: + logger.error(f"Error cleaning up file {compressed_path}: {str(cleanup_error)}") + raise e + except Exception as e: + logger.error(f"Error in play_compressed: {str(e)}", exc_info=True) + await ctx.respond(f"Error playing compressed file: {str(e)}", ephemeral=True) - @discord.slash_command(name="grajcuj_zkompresowane", description="grajcujmy wszyscy w wysokiej jakosci!") - async def play_compressed(self, ctx: discord.ApplicationContext, url: str = discord.Option(str, "co zagrać??", required=True)): - await ctx.defer() - url, title = await play_utils.PlayUtils(self.bot).get_url(ctx, url) - filename = await play_utils.PlayUtils(self.bot).download(ctx, url) - filename = await play_utils.PlayUtils(self.bot).compress(ctx, filename) - await play_utils.PlayUtils(self.bot).play(ctx, filename, title) + button.callback = button_callback + view.add_item(button) + await ctx.respond(embed=embed, view=view, ephemeral=True) + else: + await ctx.respond("Nie znaleziono pliku do odtworzenia", ephemeral=True) + else: + # For YouTube/SoundCloud + try: + url, title = await play_utils.PlayUtils(self.bot).get_url(ctx, query) + if not url: + return + + filename = await play_utils.PlayUtils(self.bot).download(ctx, url) + if not filename: + return + + # Compress the downloaded file + compressed_filename = await play_utils.PlayUtils(self.bot).compress(ctx, filename) + if compressed_filename: + await play_utils.PlayUtils(self.bot).play(ctx, compressed_filename, title) + except Exception as e: + logger.error(f"Error in play_compressed: {str(e)}") + await ctx.respond(f"Error: {str(e)}", ephemeral=True) @discord.slash_command(name="grajcuj_plik", description="grajcuj plik") async def play_file(self, ctx: discord.ApplicationContext, attachment: discord.Attachment = discord.Option(discord.Attachment, "co zagrać??", required=True)): diff --git a/cogs/music/play_utils.py b/cogs/music/play_utils.py index 6fa0edd..b3d81a8 100644 --- a/cogs/music/play_utils.py +++ b/cogs/music/play_utils.py @@ -108,30 +108,70 @@ class PlayUtils: async def compress(ctx, filename): if not filename: await ctx.respond("dzie wideło", ephemeral=True) - return + return None - ffmpeg_command = f"ffmpeg -i {filename} -ac 1 -b:a 500 -acodec libopus {filename}" + # Create temp directory if it doesn't exist + temp_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'temp', 'compressed') + os.makedirs(temp_dir, exist_ok=True) + + # Create a temporary output filename in the temp directory + base_name = os.path.basename(filename) + output_filename = os.path.join(temp_dir, f"{os.path.splitext(base_name)[0]}_compressed.opus") + + # Ensure the output directory exists + os.makedirs(os.path.dirname(output_filename) or '.', exist_ok=True) + + # Use proper quoting for filenames with spaces + ffmpeg_command = [ + 'ffmpeg', + '-y', # Overwrite output file if it exists + '-i', filename, + '-ac', '1', # Mono audio + '-b:a', '500', # 500k bitrate + '-acodec', 'libopus', + output_filename + ] + try: - process = await asyncio.create_subprocess_shell( - ffmpeg_command, + logger.info(f"Running FFmpeg command: {' '.join(ffmpeg_command)}") + process = await asyncio.create_subprocess_exec( + *ffmpeg_command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) + stdout, stderr = await process.communicate() + if process.returncode != 0: - logger.error(f"FFmpeg error: {stderr.decode()}") + error_msg = stderr.decode() + logger.error(f"FFmpeg error ({process.returncode}): {error_msg}") await ctx.respond("eror kompresujync pilk", ephemeral=True) - else: - logger.info(f"Compressed file: {filename}") + return None + + if not os.path.exists(output_filename): + logger.error(f"Output file was not created: {output_filename}") + await ctx.respond("eror kompresujync pilk - output file not created", ephemeral=True) + return None + + logger.info(f"Successfully compressed file: {output_filename}") + return output_filename + except Exception as e: - logger.error(f"Error compressing file: {e}") - await ctx.respond("eror kompresujync pilk", ephemeral=True) + logger.error(f"Error compressing file: {str(e)}", exc_info=True) + await ctx.respond(f"eror kompresujync pilk: {str(e)}", ephemeral=True) + return None @staticmethod - async def play(ctx, filepath): + async def play(ctx, filepath, delete_after_play=False): # Check if user is in a voice channel if not ctx.author.voice or not ctx.author.voice.channel: await ctx.respond("Musisz być na kanale głosowym, żebym mógł dołączyć!", ephemeral=True) + if delete_after_play and os.path.exists(filepath): + try: + os.remove(filepath) + logger.info(f"Cleaned up temporary file: {filepath}") + except Exception as e: + logger.error(f"Error cleaning up file {filepath}: {str(e)}") return logger.info(f"Attempting to play: {filepath}") -- cgit v1.2.3