aboutsummaryrefslogtreecommitdiffstats
path: root/cogs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--cogs/music/play.py84
-rw-r--r--cogs/music/play_utils.py95
2 files changed, 105 insertions, 74 deletions
diff --git a/cogs/music/play.py b/cogs/music/play.py
index c9abe4b..2c5b641 100644
--- a/cogs/music/play.py
+++ b/cogs/music/play.py
@@ -137,60 +137,68 @@ class Play(commands.Cog):
137 ctx: discord.ApplicationContext, 137 ctx: discord.ApplicationContext,
138 file: str = discord.Option( 138 file: str = discord.Option(
139 str, 139 str,
140 "Wybierz plik do odtworzenia", 140 "wyszukaj plik z serwera",
141 autocomplete=_search_autocomplete, 141 autocomplete=_search_autocomplete,
142 required=True 142 required=False
143 ), 143 ),
144 source: str = discord.Option( 144 stream: str = discord.Option(
145 str, 145 str,
146 "Źródło dźwięku", 146 "odtwórz stream internetowy (e.g. radio)",
147 choices=["local", "soundcloud", "youtube"], 147 required=False
148 default="local"
149 ), 148 ),
150 compressed: bool = discord.Option( 149 compressed: bool = discord.Option(
151 bool, 150 bool,
152 "Czy skompresować dźwięk?", 151 "Czy skompresować dźwięk? (tylko dla plików lokalnych)",
153 default=False 152 default=False
154 ) 153 )
155 ): 154 ):
156 """Odtwarza wybrany plik dźwiękowy z opcjonalną kompresją.""" 155 """Odtwarza wybrany plik dźwiękowy lub stream internetowy."""
157 await ctx.defer() 156 await ctx.defer()
158 157
159 if source != "local": 158 # Check if either file or stream is provided
160 await ctx.respond("Tylko źródło 'local' jest obecnie obsługiwane w tej komendzie.", ephemeral=True) 159 if not file and not stream:
160 await ctx.respond("Musisz podać plik do odtworzenia lub adres streamu.", ephemeral=True)
161 return 161 return
162 162
163 # Look up the full path from the display name 163 if file and stream:
164 full_path = self.path_mapping.get(file, file) 164 await ctx.respond("Możesz podać tylko plik LUB stream, nie oba naraz.", ephemeral=True)
165
166 if not os.path.exists(full_path):
167 logger.warning(f"File not found: {full_path} (display name: {file})")
168 await ctx.respond("Wybrany plik nie istnieje.", ephemeral=True)
169 return 165 return
170 166
171 try: 167 try:
172 if compressed: 168 if stream:
173 # Compress the file before playing 169 # Handle internet stream
174 compressed_path = await play_utils.PlayUtils(self.bot).compress(ctx, full_path) 170 await play_utils.PlayUtils(self.bot).play(ctx, stream)
175 if not compressed_path:
176 await ctx.respond("Nie udało się skompresować pliku.", ephemeral=True)
177 return
178
179 try:
180 await play_utils.PlayUtils(self.bot).play(ctx, compressed_path, delete_after_play=True)
181 except Exception as e:
182 # Clean up the compressed file if play fails
183 if os.path.exists(compressed_path):
184 try:
185 os.remove(compressed_path)
186 logger.info(f"Cleaned up temporary file after play error: {compressed_path}")
187 except Exception as cleanup_error:
188 logger.error(f"Error cleaning up file {compressed_path}: {str(cleanup_error)}")
189 raise e
190 else: 171 else:
191 await play_utils.PlayUtils(self.bot).play(ctx, full_path) 172 # Handle local file
173 full_path = self.path_mapping.get(file, file)
174
175 if not os.path.exists(full_path):
176 logger.warning(f"File not found: {full_path} (display name: {file})")
177 await ctx.respond("Wybrany plik nie istnieje.", ephemeral=True)
178 return
179
180 if compressed:
181 # Compress the file before playing
182 compressed_path = await play_utils.PlayUtils(self.bot).compress(ctx, full_path)
183 if not compressed_path:
184 await ctx.respond("Nie udało się skompresować pliku.", ephemeral=True)
185 return
186
187 try:
188 await play_utils.PlayUtils(self.bot).play(ctx, compressed_path, delete_after_play=True)
189 except Exception as e:
190 # Clean up the compressed file if play fails
191 if os.path.exists(compressed_path):
192 try:
193 os.remove(compressed_path)
194 logger.info(f"Cleaned up temporary file after play error: {compressed_path}")
195 except Exception as cleanup_error:
196 logger.error(f"Error cleaning up file {compressed_path}: {str(cleanup_error)}")
197 raise e
198 else:
199 await play_utils.PlayUtils(self.bot).play(ctx, full_path)
192 except Exception as e: 200 except Exception as e:
193 logger.error(f"Error playing file: {str(e)}", exc_info=True) 201 logger.error(f"Error playing: {str(e)}", exc_info=True)
194 await ctx.respond(f"Wystąpił błąd podczas odtwarzania: {str(e)}", ephemeral=True) 202 await ctx.respond(f"Wystąpił błąd podczas odtwarzania: {str(e)}", ephemeral=True)
195 203
196 @discord.slash_command(name="grajcuj_plik", description="grajcuj plik") 204 @discord.slash_command(name="grajcuj_plik", description="grajcuj plik")
diff --git a/cogs/music/play_utils.py b/cogs/music/play_utils.py
index 8de7543..0614157 100644
--- a/cogs/music/play_utils.py
+++ b/cogs/music/play_utils.py
@@ -226,34 +226,49 @@ class PlayUtils:
226 return None 226 return None
227 227
228 @staticmethod 228 @staticmethod
229 async def play(ctx, filepath, delete_after_play=False): 229 async def play(ctx, source, delete_after_play=False):
230 """Play audio from a local file or URL.
231
232 Args:
233 ctx: Discord context
234 source: Either a local file path or a URL to stream from
235 delete_after_play: If True, delete the file after playing (only for local files)
236 """
230 # Check if user is in a voice channel 237 # Check if user is in a voice channel
231 if not ctx.author.voice or not ctx.author.voice.channel: 238 if not ctx.author.voice or not ctx.author.voice.channel:
232 await ctx.respond("Musisz być na kanale głosowym, żebym mógł dołączyć!", ephemeral=True) 239 await ctx.respond("Musisz być na kanale głosowym, żebym mógł dołączyć!", ephemeral=True)
233 if delete_after_play and os.path.exists(filepath): 240 if delete_after_play and os.path.exists(source):
234 try: 241 try:
235 os.remove(filepath) 242 os.remove(source)
236 logger.info(f"Cleaned up temporary file: {filepath}") 243 logger.info(f"Cleaned up temporary file: {source}")
237 except Exception as e: 244 except Exception as e:
238 logger.error(f"Error cleaning up file {filepath}: {str(e)}") 245 logger.error(f"Error cleaning up file {source}: {str(e)}")
239 return 246 return
240 247
241 logger.info(f"Attempting to play: {filepath}") 248 is_url = source.startswith(('http://', 'https://'))
242
243 # Get absolute path and verify file exists
244 if not os.path.isabs(filepath):
245 filepath = os.path.abspath(filepath)
246 logger.debug(f"Converted to absolute path: {filepath}")
247 249
248 if not os.path.exists(filepath): 250 if not is_url:
249 error_msg = f"File not found: {filepath}" 251 # Handle local file
250 logger.error(error_msg) 252 logger.info(f"Attempting to play local file: {source}")
251 await ctx.respond("Nie znaleziono pliku do odtworzenia", ephemeral=True)
252 return
253 253
254 filename = os.path.basename(filepath) 254 # Get absolute path and verify file exists
255 logger.info(f"Playing file: {filename} (from: {filepath})") 255 if not os.path.isabs(source):
256 256 source = os.path.abspath(source)
257 logger.debug(f"Converted to absolute path: {source}")
258
259 if not os.path.exists(source):
260 error_msg = f"File not found: {source}"
261 logger.error(error_msg)
262 await ctx.respond("Nie znaleziono pliku do odtworzenia", ephemeral=True)
263 return
264
265 filename = os.path.basename(source)
266 display_name = os.path.splitext(filename)[0]
267 else:
268 # Handle URL
269 logger.info(f"Attempting to play stream from URL: {source}")
270 display_name = source # Use URL as display name for streams
271
257 # Get or create voice client 272 # Get or create voice client
258 vc = ctx.voice_client 273 vc = ctx.voice_client
259 target_channel = ctx.author.voice.channel 274 target_channel = ctx.author.voice.channel
@@ -275,23 +290,31 @@ class PlayUtils:
275 vc.stop() 290 vc.stop()
276 291
277 # Play the audio 292 # Play the audio
278 source = discord.FFmpegPCMAudio(source=filepath) 293 if is_url:
279 vc.play(source) 294 # For URLs, use FFmpeg with the direct URL
280 295 ffmpeg_options = {
281 try: 296 'options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5',
282 metadata = mutagen.File(filepath) 297 'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5'
283 title = metadata.get('title', None) 298 }
284 logger.debug(f"Read metadata - title: {title}") 299 source = discord.FFmpegPCMAudio(source, **ffmpeg_options)
285 except Exception as e: 300 else:
286 logger.warning(f"Could not read metadata from {filepath}: {str(e)}") 301 # For local files, use default options
287 title = None 302 source = discord.FFmpegPCMAudio(source=source)
288 303
289 if title is None: 304 # Try to get metadata for local files
290 title = os.path.splitext(os.path.basename(filepath))[0] 305 try:
291 logger.debug(f"Using filename as title: {title}") 306 metadata = mutagen.File(source)
307 if metadata:
308 title = metadata.get('title', [None])[0] if hasattr(metadata, 'get') else None
309 if title:
310 display_name = title
311 logger.debug(f"Read metadata - title: {title}")
312 except Exception as e:
313 logger.warning(f"Could not read metadata from {source}: {str(e)}")
292 314
293 logger.info(f"Playing file: {title}") 315 vc.play(source)
294 await ctx.respond(f"grom: {title}") 316 logger.info(f"Playing: {display_name}")
317 await ctx.respond(f"grom: {display_name}")
295 318
296 except Exception as e: 319 except Exception as e:
297 logger.error(f"Error in play: {str(e)}", exc_info=True) 320 logger.error(f"Error in play: {str(e)}", exc_info=True)