diff options
Diffstat (limited to 'cogs/music/play.py')
| -rw-r--r-- | cogs/music/play.py | 139 |
1 files changed, 115 insertions, 24 deletions
diff --git a/cogs/music/play.py b/cogs/music/play.py index 1eafbca..bf9d081 100644 --- a/cogs/music/play.py +++ b/cogs/music/play.py | |||
| @@ -1,53 +1,144 @@ | |||
| 1 | import discord | 1 | import discord |
| 2 | from discord.ext import commands | 2 | from discord.ext import commands |
| 3 | import asyncio | 3 | import asyncio |
| 4 | import yt_dlp | ||
| 4 | from youtubesearchpython import VideosSearch | 5 | from youtubesearchpython import VideosSearch |
| 5 | 6 | ||
| 7 | # Suppress noise about console usage from yt-dlp | ||
| 8 | yt_dlp.utils.bug_reports_message = lambda: '' | ||
| 9 | |||
| 6 | class Play(commands.Cog): | 10 | class Play(commands.Cog): |
| 7 | def __init__(self, bot): | 11 | def __init__(self, bot): |
| 8 | self.bot = bot | 12 | self.bot = bot |
| 13 | self.queues = {} | ||
| 14 | self.ydl_opts = { | ||
| 15 | 'format': 'bestaudio/best', | ||
| 16 | 'postprocessors': [{ | ||
| 17 | 'key': 'FFmpegExtractAudio', | ||
| 18 | 'preferredcodec': 'mp3', | ||
| 19 | 'preferredquality': '192', | ||
| 20 | }], | ||
| 21 | 'noplaylist': True, | ||
| 22 | 'quiet': True, | ||
| 23 | 'no_warnings': True, | ||
| 24 | 'default_search': 'auto', | ||
| 25 | 'source_address': '0.0.0.0', | ||
| 26 | } | ||
| 27 | |||
| 28 | def get_queue(self, guild_id): | ||
| 29 | return self.queues.setdefault(guild_id, []) | ||
| 30 | |||
| 31 | async def play_next(self, ctx, vc, guild_id): | ||
| 32 | queue = self.get_queue(guild_id) | ||
| 33 | if queue: | ||
| 34 | title, url = queue[0] | ||
| 35 | |||
| 36 | def after_playing(error): | ||
| 37 | if queue: | ||
| 38 | queue.pop(0) | ||
| 39 | if queue: | ||
| 40 | next_title, next_url = queue[0] | ||
| 41 | if not vc.is_playing() and not vc.is_paused(): | ||
| 42 | asyncio.run_coroutine_threadsafe( | ||
| 43 | self.play_audio(ctx, vc, next_url, next_title, guild_id), | ||
| 44 | self.bot.loop | ||
| 45 | ) | ||
| 46 | else: | ||
| 47 | asyncio.run_coroutine_threadsafe( | ||
| 48 | vc.disconnect(), | ||
| 49 | self.bot.loop | ||
| 50 | ) | ||
| 51 | |||
| 52 | if not vc.is_playing() and not vc.is_paused(): | ||
| 53 | await self.play_audio(ctx, vc, url, title, guild_id, after=after_playing) | ||
| 54 | |||
| 55 | async def play_audio(self, ctx, vc, url, title, guild_id, after=None): | ||
| 56 | try: | ||
| 57 | with yt_dlp.YoutubeDL(self.ydl_opts) as ydl: | ||
| 58 | info = await self.bot.loop.run_in_executor( | ||
| 59 | None, | ||
| 60 | lambda: ydl.extract_info(url, download=False) | ||
| 61 | ) | ||
| 62 | if 'entries' in info: | ||
| 63 | info = info['entries'][0] | ||
| 64 | |||
| 65 | url2 = info['url'] | ||
| 66 | |||
| 67 | FFMPEG_OPTIONS = { | ||
| 68 | 'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', | ||
| 69 | 'options': '-vn' | ||
| 70 | } | ||
| 71 | |||
| 72 | source = discord.FFmpegPCMAudio(url2, **FFMPEG_OPTIONS) | ||
| 73 | vc.play(source, after=after) | ||
| 74 | await ctx.followup.send(f"gra: {title}") | ||
| 75 | except Exception as e: | ||
| 76 | await ctx.followup.send(f"Wystąpił błąd podczas odtwarzania: {str(e)}") | ||
| 77 | # Try to play next in queue if there's an error | ||
| 78 | if after: | ||
| 79 | after(None) | ||
| 9 | 80 | ||
| 10 | @commands.slash_command(name="grajcuj", description="grajcuj melodyje") | 81 | @commands.slash_command(name="grajcuj", description="grajcuj melodyje") |
| 11 | async def play(self, ctx, *, search: str): | 82 | async def play(self, ctx, *, search: str): |
| 12 | await ctx.defer() | 83 | await ctx.defer() |
| 84 | |||
| 13 | if not (ctx.author.voice and ctx.author.voice.channel): | 85 | if not (ctx.author.voice and ctx.author.voice.channel): |
| 14 | await ctx.followup.send("nie jesteś w kanale głosowym") | 86 | await ctx.followup.send("nie jesteś w kanale głosowym") |
| 15 | return | 87 | return |
| 88 | |||
| 16 | channel = ctx.author.voice.channel | 89 | channel = ctx.author.voice.channel |
| 17 | if ctx.voice_client is not None: | 90 | vc = ctx.voice_client or await channel.connect() |
| 18 | vc = ctx.voice_client | 91 | |
| 92 | if vc.channel != channel: | ||
| 19 | await vc.move_to(channel) | 93 | await vc.move_to(channel) |
| 20 | else: | 94 | |
| 21 | vc = await channel.connect() | 95 | # Search for the video |
| 22 | videos_search = VideosSearch(search, limit=1) | 96 | videos_search = VideosSearch(search, limit=1) |
| 23 | result = videos_search.result() | 97 | result = videos_search.result() |
| 98 | |||
| 24 | if not result['result']: | 99 | if not result['result']: |
| 25 | await ctx.followup.send("nie znaleziono wideo dla twojego wyszukiwania.", ephemeral=True) | 100 | await ctx.followup.send("nie znaleziono wideo dla twojego wyszukiwania.", ephemeral=True) |
| 26 | return | 101 | return |
| 102 | |||
| 27 | video = result['result'][0] | 103 | video = result['result'][0] |
| 28 | video_id = video['id'] | ||
| 29 | title = video['title'] | 104 | title = video['title'] |
| 30 | url = video['link'] | 105 | url = video['link'] |
| 31 | proc = await asyncio.create_subprocess_exec( | 106 | |
| 32 | "yt-dlp", "-f", "bestaudio", "-g", "--no-cache-dir", url, | 107 | # Add to queue |
| 33 | stdout=asyncio.subprocess.PIPE | 108 | queue = self.get_queue(ctx.guild.id) |
| 34 | ) | 109 | queue.append((title, url)) |
| 35 | stdout, _ = await proc.communicate() | 110 | |
| 36 | stream_url = stdout.decode().strip() | 111 | # If nothing is playing, start playing |
| 37 | FFMPEG_OPTIONS = { | 112 | if not vc.is_playing() and not vc.is_paused(): |
| 38 | 'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', | 113 | await self.play_next(ctx, vc, ctx.guild.id) |
| 39 | 'options': '-vn' | 114 | else: |
| 40 | } | 115 | await ctx.followup.send(f"dodano do kolejki: {title}") |
| 41 | audio = discord.FFmpegPCMAudio( | 116 | |
| 42 | stream_url, | 117 | @commands.slash_command(name="stop", description="zatrzymaj odtwarzanie i wyczyść kolejkę") |
| 43 | before_options=FFMPEG_OPTIONS['before_options'], | 118 | async def stop(self, ctx): |
| 44 | options=FFMPEG_OPTIONS['options'] | 119 | if ctx.voice_client: |
| 45 | ) | 120 | queue = self.get_queue(ctx.guild.id) |
| 46 | if not vc.is_playing(): | 121 | queue.clear() |
| 47 | vc.play(audio) | 122 | await ctx.voice_client.disconnect() |
| 48 | await ctx.followup.send(f"gra: {title}") | 123 | await ctx.respond("Zatrzymano odtwarzanie i wyczyszczono kolejkę") |
| 124 | else: | ||
| 125 | await ctx.respond("Nie jestem połączony z kanałem głosowym") | ||
| 126 | |||
| 127 | @commands.slash_command(name="pauza", description="wstrzymaj odtwarzanie") | ||
| 128 | async def pause(self, ctx): | ||
| 129 | if ctx.voice_client and ctx.voice_client.is_playing(): | ||
| 130 | ctx.voice_client.pause() | ||
| 131 | await ctx.respond("Wstrzymano odtwarzanie") | ||
| 132 | else: | ||
| 133 | await ctx.respond("Nic nie jest teraz odtwarzane") | ||
| 134 | |||
| 135 | @commands.slash_command(name="wznow", description="wznów odtwarzanie") | ||
| 136 | async def resume(self, ctx): | ||
| 137 | if ctx.voice_client and ctx.voice_client.is_paused(): | ||
| 138 | ctx.voice_client.resume() | ||
| 139 | await ctx.respond("Wznowiono odtwarzanie") | ||
| 49 | else: | 140 | else: |
| 50 | await ctx.followup.send(f"dodano do koleyjki: {title}") | 141 | await ctx.respond("Nic nie jest teraz wstrzymane") |
| 51 | 142 | ||
| 52 | def setup(bot): | 143 | def setup(bot): |
| 53 | bot.add_cog(Play(bot)) \ No newline at end of file | 144 | bot.add_cog(Play(bot)) \ No newline at end of file |
