diff options
Diffstat (limited to '')
| -rw-r--r-- | cogs/music/play.py | 84 | ||||
| -rw-r--r-- | cogs/music/play_utils.py | 95 |
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) |
