diff options
Diffstat (limited to '')
| -rw-r--r-- | cogs/music/audio_playback.py | 14 | ||||
| -rw-r--r-- | cogs/music/play_commands.py | 91 |
2 files changed, 43 insertions, 62 deletions
diff --git a/cogs/music/audio_playback.py b/cogs/music/audio_playback.py index 3d4f453..d23809a 100644 --- a/cogs/music/audio_playback.py +++ b/cogs/music/audio_playback.py | |||
| @@ -5,8 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | import os | 6 | import os |
| 7 | import logging | 7 | import logging |
| 8 | from pathlib import Path | 8 | import urllib.parse |
| 9 | |||
| 10 | import discord | 9 | import discord |
| 11 | import mutagen | 10 | import mutagen |
| 12 | 11 | ||
| @@ -74,13 +73,17 @@ class AudioPlaybackManager: | |||
| 74 | async def prepare_audio_source(source, is_url, display_name): | 73 | async def prepare_audio_source(source, is_url, display_name): |
| 75 | """Prepare audio source for playback. Returns (source, final_display_name).""" | 74 | """Prepare audio source for playback. Returns (source, final_display_name).""" |
| 76 | if is_url: | 75 | if is_url: |
| 77 | # For URLs, use FFmpeg with the direct URL | 76 | # For URLs, use FFmpeg with the direct URL and extract clean filename |
| 78 | ffmpeg_options = { | 77 | ffmpeg_options = { |
| 79 | 'options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', | 78 | 'options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', |
| 80 | 'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5' | 79 | 'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5' |
| 81 | } | 80 | } |
| 82 | audio_source = discord.FFmpegPCMAudio(source, **ffmpeg_options) | 81 | audio_source = discord.FFmpegPCMAudio(source, **ffmpeg_options) |
| 83 | return audio_source, display_name | 82 | # Extract clean filename for URL (remove extension and decode URL encoding) |
| 83 | filename = os.path.basename(source.split('?')[0]) # Remove query parameters | ||
| 84 | filename = urllib.parse.unquote(filename) # Decode URL encoding | ||
| 85 | clean_filename = os.path.splitext(filename)[0] # Remove file extension | ||
| 86 | return audio_source, clean_filename | ||
| 84 | else: | 87 | else: |
| 85 | # For local files, use default options | 88 | # For local files, use default options |
| 86 | audio_source = discord.FFmpegPCMAudio(source=source) | 89 | audio_source = discord.FFmpegPCMAudio(source=source) |
| @@ -93,8 +96,11 @@ class AudioPlaybackManager: | |||
| 93 | if title: | 96 | if title: |
| 94 | display_name = title | 97 | display_name = title |
| 95 | logger.debug(f"Read metadata - title: {title}") | 98 | logger.debug(f"Read metadata - title: {title}") |
| 99 | else: | ||
| 100 | display_name = os.path.splitext(os.path.basename(source))[0] | ||
| 96 | except Exception as e: | 101 | except Exception as e: |
| 97 | logger.warning(f"Could not read metadata from {source}: {str(e)}") | 102 | logger.warning(f"Could not read metadata from {source}: {str(e)}") |
| 103 | display_name = os.path.splitext(os.path.basename(source))[0] | ||
| 98 | 104 | ||
| 99 | return audio_source, display_name | 105 | return audio_source, display_name |
| 100 | 106 | ||
diff --git a/cogs/music/play_commands.py b/cogs/music/play_commands.py index 23808d3..a06c636 100644 --- a/cogs/music/play_commands.py +++ b/cogs/music/play_commands.py | |||
| @@ -78,7 +78,7 @@ class PlayCommands(commands.Cog): | |||
| 78 | """Autocomplete handler for search queries.""" | 78 | """Autocomplete handler for search queries.""" |
| 79 | query = ctx.value.lower() | 79 | query = ctx.value.lower() |
| 80 | try: | 80 | try: |
| 81 | results = await search_utils.UniversalSearcher.search_copyparty_autocomplete(query, max_results=25) | 81 | results = await search_utils.UniversalSearcher.search_copyparty_autocomplete_audio(query, max_results=25) |
| 82 | choices = [] | 82 | choices = [] |
| 83 | self.path_mapping = {} # Reset the mapping for each new query | 83 | self.path_mapping = {} # Reset the mapping for each new query |
| 84 | 84 | ||
| @@ -123,65 +123,40 @@ class PlayCommands(commands.Cog): | |||
| 123 | await audio_playback.AudioPlaybackManager.play(ctx, stream) | 123 | await audio_playback.AudioPlaybackManager.play(ctx, stream) |
| 124 | 124 | ||
| 125 | async def _handle_compressed_file_play(self, ctx: discord.ApplicationContext, url: str) -> None: | 125 | async def _handle_compressed_file_play(self, ctx: discord.ApplicationContext, url: str) -> None: |
| 126 | """Handle compressed file playback with cleanup.""" | 126 | """Download from copyparty, compress, then play locally.""" |
| 127 | is_url = url.startswith(('http://', 'https://')) | 127 | import aiohttp |
| 128 | 128 | local_path = None | |
| 129 | if is_url: | 129 | compressed_path = None |
| 130 | import aiohttp | 130 | try: |
| 131 | local_path = None | 131 | async with aiohttp.ClientSession() as session: |
| 132 | try: | 132 | async with session.get(url) as response: |
| 133 | async with aiohttp.ClientSession() as session: | 133 | if response.status != 200: |
| 134 | async with session.get(url) as response: | 134 | await ctx.respond("Nie udało się pobrać pliku.", ephemeral=True) |
| 135 | if response.status != 200: | 135 | return |
| 136 | await ctx.respond("Nie udało się pobrać pliku.", ephemeral=True) | ||
| 137 | return | ||
| 138 | |||
| 139 | filename = url.split('/')[-1].split('?')[0] | ||
| 140 | local_path = os.path.join(self.temp_dir, filename) | ||
| 141 | os.makedirs(self.temp_dir, exist_ok=True) | ||
| 142 | |||
| 143 | with open(local_path, 'wb') as f: | ||
| 144 | async for chunk in response.content.iter_chunked(8192): | ||
| 145 | f.write(chunk) | ||
| 146 | |||
| 147 | compressed_path = await media_processing.AudioCompressor.compress(ctx, local_path) | ||
| 148 | if not compressed_path: | ||
| 149 | await ctx.respond("Nie udało się skompresować pliku.", ephemeral=True) | ||
| 150 | return | ||
| 151 | 136 | ||
| 152 | try: | 137 | filename = url.split('/')[-1].split('?')[0] |
| 153 | await audio_playback.AudioPlaybackManager.play(ctx, compressed_path, delete_after_play=True) | 138 | filename = urllib.parse.unquote(filename) |
| 154 | except Exception as e: | 139 | local_path = os.path.join(self.temp_dir, filename) |
| 155 | if os.path.exists(compressed_path): | 140 | os.makedirs(self.temp_dir, exist_ok=True) |
| 156 | try: | 141 | |
| 157 | os.remove(compressed_path) | 142 | with open(local_path, 'wb') as f: |
| 158 | except Exception: | 143 | async for chunk in response.content.iter_chunked(8192): |
| 159 | pass | 144 | f.write(chunk) |
| 160 | raise e | 145 | |
| 161 | finally: | 146 | compressed_path = await media_processing.AudioCompressor.compress(ctx, local_path) |
| 162 | if os.path.exists(local_path): | ||
| 163 | try: | ||
| 164 | os.remove(local_path) | ||
| 165 | except Exception: | ||
| 166 | pass | ||
| 167 | except Exception as e: | ||
| 168 | logger.error(f"Error downloading file from URL: {str(e)}", exc_info=True) | ||
| 169 | await ctx.respond(f"Błąd pobierania pliku: {str(e)}", ephemeral=True) | ||
| 170 | else: | ||
| 171 | compressed_path = await media_processing.AudioCompressor.compress(ctx, url) | ||
| 172 | if not compressed_path: | 147 | if not compressed_path: |
| 173 | await ctx.respond("Nie udało się skompresować pliku.", ephemeral=True) | ||
| 174 | return | 148 | return |
| 175 | 149 | ||
| 176 | try: | 150 | await audio_playback.AudioPlaybackManager.play(ctx, compressed_path, delete_after_play=True) |
| 177 | await audio_playback.AudioPlaybackManager.play(ctx, compressed_path, delete_after_play=True) | 151 | except Exception as e: |
| 178 | except Exception as e: | 152 | logger.error(f"Error in compressed playback: {str(e)}", exc_info=True) |
| 179 | if os.path.exists(compressed_path): | 153 | await ctx.respond(f"Błąd: {str(e)}", ephemeral=True) |
| 180 | try: | 154 | finally: |
| 181 | os.remove(compressed_path) | 155 | if local_path and os.path.exists(local_path): |
| 182 | except Exception: | 156 | try: |
| 183 | pass | 157 | os.remove(local_path) |
| 184 | raise e | 158 | except Exception: |
| 159 | pass | ||
| 185 | 160 | ||
| 186 | async def _handle_local_file_play(self, ctx: discord.ApplicationContext, file: str, compressed: bool) -> None: | 161 | async def _handle_local_file_play(self, ctx: discord.ApplicationContext, file: str, compressed: bool) -> None: |
| 187 | """Handle file playback from copyparty URL.""" | 162 | """Handle file playback from copyparty URL.""" |
| @@ -209,14 +184,14 @@ class PlayCommands(commands.Cog): | |||
| 209 | ), | 184 | ), |
| 210 | compressed: bool = discord.Option( | 185 | compressed: bool = discord.Option( |
| 211 | bool, | 186 | bool, |
| 212 | "Czy skompresować dźwięk? (tylko dla plików lokalnych)", | 187 | "Czy skompresować dźwięk?", |
| 213 | default=False | 188 | default=False |
| 214 | ) | 189 | ) |
| 215 | ): | 190 | ): |
| 216 | """Odtwarza wybrany plik dźwiękowy lub stream internetowy.""" | 191 | """Odtwarza wybrany plik dźwiękowy lub stream internetowy.""" |
| 217 | await ctx.defer() | 192 | await ctx.defer() |
| 218 | 193 | ||
| 219 | if not self._validate_play_inputs(file, stream, ctx): | 194 | if not await self._validate_play_inputs(file, stream, ctx): |
| 220 | return | 195 | return |
| 221 | 196 | ||
| 222 | try: | 197 | try: |
