aboutsummaryrefslogtreecommitdiffstats
path: root/cogs/music
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--cogs/music/play.py77
-rw-r--r--cogs/music/play_utils.py60
2 files changed, 120 insertions, 17 deletions
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):
48 else: 48 else:
49 await ctx.respond("Nie znaleziono pliku do odtworzenia", ephemeral=True) 49 await ctx.respond("Nie znaleziono pliku do odtworzenia", ephemeral=True)
50 50
51 @discord.slash_command(name="grajcuj_zkompresowane", description="grajcujmy wszyscy w wysokiej jakosci!")
52 async def play_compressed(
53 self,
54 ctx: discord.ApplicationContext,
55 query: str = discord.Option(str, "co zagrać??", required=True),
56 source: str = discord.Option(str, "skąd zagrać??", required=True,
57 choices=["local", "soundcloud", "youtube"])
58 ):
59 await ctx.defer(ephemeral=True)
60 if source == "local":
61 candidates = await play_utils.PlayUtils(self.bot).search_local(query)
62 if candidates:
63 embed = discord.Embed(title="Wyniki wyszukiwania", description="Wybierz plik do odtworzenia:",
64 color=discord.Color.blue())
65 view = discord.ui.View(disable_on_timeout=True)
66 for i, candidate in enumerate(candidates, start=1):
67 filename = os.path.basename(candidate)
68 metadata = None
69 if os.path.splitext(filename)[1].lower() in ['.mp3', '.wav', '.opus']:
70 metadata = mutagen.File(candidate)
71 title = metadata.get('title', None)
72 if metadata and title:
73 embed.add_field(name=f"{i}. {title}", value="\u200b", inline=False)
74 else:
75 embed.add_field(name=f"{i}. {filename}", value="\u200b", inline=False)
76 button = discord.ui.Button(label=f"Play {i}")
51 77
78 async def button_callback(interaction: discord.Interaction, candidate=candidate):
79 await interaction.response.defer(ephemeral=True)
80 try:
81 await ctx.interaction.delete_original_response()
82 # Compress the file before playing
83 compressed_path = await play_utils.PlayUtils(self.bot).compress(ctx, candidate)
84 if compressed_path:
85 try:
86 await play_utils.PlayUtils(self.bot).play(ctx, compressed_path, delete_after_play=True)
87 except Exception as e:
88 # Clean up the compressed file if play fails
89 if os.path.exists(compressed_path):
90 try:
91 os.remove(compressed_path)
92 logger.info(f"Cleaned up temporary file after play error: {compressed_path}")
93 except Exception as cleanup_error:
94 logger.error(f"Error cleaning up file {compressed_path}: {str(cleanup_error)}")
95 raise e
96 except Exception as e:
97 logger.error(f"Error in play_compressed: {str(e)}", exc_info=True)
98 await ctx.respond(f"Error playing compressed file: {str(e)}", ephemeral=True)
52 99
53 @discord.slash_command(name="grajcuj_zkompresowane", description="grajcujmy wszyscy w wysokiej jakosci!") 100 button.callback = button_callback
54 async def play_compressed(self, ctx: discord.ApplicationContext, url: str = discord.Option(str, "co zagrać??", required=True)): 101 view.add_item(button)
55 await ctx.defer() 102 await ctx.respond(embed=embed, view=view, ephemeral=True)
56 url, title = await play_utils.PlayUtils(self.bot).get_url(ctx, url) 103 else:
57 filename = await play_utils.PlayUtils(self.bot).download(ctx, url) 104 await ctx.respond("Nie znaleziono pliku do odtworzenia", ephemeral=True)
58 filename = await play_utils.PlayUtils(self.bot).compress(ctx, filename) 105 else:
59 await play_utils.PlayUtils(self.bot).play(ctx, filename, title) 106 # For YouTube/SoundCloud
107 try:
108 url, title = await play_utils.PlayUtils(self.bot).get_url(ctx, query)
109 if not url:
110 return
111
112 filename = await play_utils.PlayUtils(self.bot).download(ctx, url)
113 if not filename:
114 return
115
116 # Compress the downloaded file
117 compressed_filename = await play_utils.PlayUtils(self.bot).compress(ctx, filename)
118 if compressed_filename:
119 await play_utils.PlayUtils(self.bot).play(ctx, compressed_filename, title)
120 except Exception as e:
121 logger.error(f"Error in play_compressed: {str(e)}")
122 await ctx.respond(f"Error: {str(e)}", ephemeral=True)
60 123
61 @discord.slash_command(name="grajcuj_plik", description="grajcuj plik") 124 @discord.slash_command(name="grajcuj_plik", description="grajcuj plik")
62 async def play_file(self, ctx: discord.ApplicationContext, attachment: discord.Attachment = discord.Option(discord.Attachment, "co zagrać??", required=True)): 125 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:
108 async def compress(ctx, filename): 108 async def compress(ctx, filename):
109 if not filename: 109 if not filename:
110 await ctx.respond("dzie wideło", ephemeral=True) 110 await ctx.respond("dzie wideło", ephemeral=True)
111 return 111 return None
112 112
113 ffmpeg_command = f"ffmpeg -i {filename} -ac 1 -b:a 500 -acodec libopus {filename}" 113 # Create temp directory if it doesn't exist
114 temp_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'temp', 'compressed')
115 os.makedirs(temp_dir, exist_ok=True)
116
117 # Create a temporary output filename in the temp directory
118 base_name = os.path.basename(filename)
119 output_filename = os.path.join(temp_dir, f"{os.path.splitext(base_name)[0]}_compressed.opus")
120
121 # Ensure the output directory exists
122 os.makedirs(os.path.dirname(output_filename) or '.', exist_ok=True)
123
124 # Use proper quoting for filenames with spaces
125 ffmpeg_command = [
126 'ffmpeg',
127 '-y', # Overwrite output file if it exists
128 '-i', filename,
129 '-ac', '1', # Mono audio
130 '-b:a', '500', # 500k bitrate
131 '-acodec', 'libopus',
132 output_filename
133 ]
134
114 try: 135 try:
115 process = await asyncio.create_subprocess_shell( 136 logger.info(f"Running FFmpeg command: {' '.join(ffmpeg_command)}")
116 ffmpeg_command, 137 process = await asyncio.create_subprocess_exec(
138 *ffmpeg_command,
117 stdout=asyncio.subprocess.PIPE, 139 stdout=asyncio.subprocess.PIPE,
118 stderr=asyncio.subprocess.PIPE 140 stderr=asyncio.subprocess.PIPE
119 ) 141 )
142
120 stdout, stderr = await process.communicate() 143 stdout, stderr = await process.communicate()
144
121 if process.returncode != 0: 145 if process.returncode != 0:
122 logger.error(f"FFmpeg error: {stderr.decode()}") 146 error_msg = stderr.decode()
147 logger.error(f"FFmpeg error ({process.returncode}): {error_msg}")
123 await ctx.respond("eror kompresujync pilk", ephemeral=True) 148 await ctx.respond("eror kompresujync pilk", ephemeral=True)
124 else: 149 return None
125 logger.info(f"Compressed file: {filename}") 150
151 if not os.path.exists(output_filename):
152 logger.error(f"Output file was not created: {output_filename}")
153 await ctx.respond("eror kompresujync pilk - output file not created", ephemeral=True)
154 return None
155
156 logger.info(f"Successfully compressed file: {output_filename}")
157 return output_filename
158
126 except Exception as e: 159 except Exception as e:
127 logger.error(f"Error compressing file: {e}") 160 logger.error(f"Error compressing file: {str(e)}", exc_info=True)
128 await ctx.respond("eror kompresujync pilk", ephemeral=True) 161 await ctx.respond(f"eror kompresujync pilk: {str(e)}", ephemeral=True)
162 return None
129 163
130 @staticmethod 164 @staticmethod
131 async def play(ctx, filepath): 165 async def play(ctx, filepath, delete_after_play=False):
132 # Check if user is in a voice channel 166 # Check if user is in a voice channel
133 if not ctx.author.voice or not ctx.author.voice.channel: 167 if not ctx.author.voice or not ctx.author.voice.channel:
134 await ctx.respond("Musisz być na kanale głosowym, żebym mógł dołączyć!", ephemeral=True) 168 await ctx.respond("Musisz być na kanale głosowym, żebym mógł dołączyć!", ephemeral=True)
169 if delete_after_play and os.path.exists(filepath):
170 try:
171 os.remove(filepath)
172 logger.info(f"Cleaned up temporary file: {filepath}")
173 except Exception as e:
174 logger.error(f"Error cleaning up file {filepath}: {str(e)}")
135 return 175 return
136 176
137 logger.info(f"Attempting to play: {filepath}") 177 logger.info(f"Attempting to play: {filepath}")