aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamlonDev <stankiewiczgracjan2019@gmail.com>2025-05-22 11:44:29 +0200
committerSamlonDev <stankiewiczgracjan2019@gmail.com>2025-05-22 11:44:29 +0200
commit42132c6013b4a324b8a4aac2c3bf26b2ae4f67a7 (patch)
tree2a65642365920841c6b2907e663ac13b681812b7
parenteasy video downloading (diff)
downloadsenator-sperma-42132c6013b4a324b8a4aac2c3bf26b2ae4f67a7.tar.gz
senator-sperma-42132c6013b4a324b8a4aac2c3bf26b2ae4f67a7.zip
use python yt-dlp instead of shell
-rw-r--r--cogs/music/play.py139
-rw-r--r--cogs/music/skip.py84
2 files changed, 164 insertions, 59 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 @@
1import discord 1import discord
2from discord.ext import commands 2from discord.ext import commands
3import asyncio 3import asyncio
4import yt_dlp
4from youtubesearchpython import VideosSearch 5from youtubesearchpython import VideosSearch
5 6
7# Suppress noise about console usage from yt-dlp
8yt_dlp.utils.bug_reports_message = lambda: ''
9
6class Play(commands.Cog): 10class 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
52def setup(bot): 143def 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
diff --git a/cogs/music/skip.py b/cogs/music/skip.py
index f48218b..67c391e 100644
--- a/cogs/music/skip.py
+++ b/cogs/music/skip.py
@@ -4,44 +4,58 @@ import discord
4class Skip(commands.Cog): 4class Skip(commands.Cog):
5 def __init__(self, bot): 5 def __init__(self, bot):
6 self.bot = bot 6 self.bot = bot
7 self.queues = {}
8
9 def get_queue(self, guild_id):
10 return self.queues.setdefault(guild_id, [])
11
12 def play_next(self, ctx, vc, guild_id):
13 queue = self.get_queue(guild_id)
14 if queue:
15 _, _, file_path = queue[0]
16 def after_playing(error=None):
17 if queue:
18 queue.pop(0)
19 if queue:
20 _, _, next_file = queue[0]
21 if not vc.is_playing():
22 vc.play(discord.FFmpegPCMAudio(next_file), after=after_playing)
23 if not vc.is_playing():
24 vc.play(discord.FFmpegPCMAudio(file_path), after=after_playing)
25 7
26 @commands.slash_command(name="pomiń", description="przeskocz do nastepnej melodyji") 8 @commands.slash_command(name="pomiń", description="przeskocz do nastepnej melodyji")
27 async def pomin(self, ctx): 9 async def pomin(self, ctx):
28 guild_id = ctx.guild.id 10 """Skip the current song and play the next one in the queue."""
29 vc = ctx.voice_client 11 vc = ctx.voice_client
30 if vc and vc.is_playing(): 12
31 queue = self.get_queue(guild_id) 13 if not vc or not vc.is_connected():
32 if queue: 14 await ctx.respond("Nie jestem połączony z kanałem głosowym.")
33 queue.pop(0) 15 return
34 if queue: 16
35 vc.stop() 17 if not vc.is_playing() and not vc.is_paused():
36 self.play_next(ctx, vc, guild_id) 18 await ctx.respond("Nic nie jest teraz odtwarzane.")
37 await ctx.respond("przeskoczono do nastepnej melodyji") 19 return
38 else: 20
39 await ctx.respond("koleyjka jest pustawa") 21 # Get the play cog
40 await vc.disconnect() 22 play_cog = self.bot.get_cog('Play')
41 else: 23 if not play_cog:
42 await ctx.respond("nic nie gra") 24 await ctx.respond("Błąd: Nie można znaleźć modułu odtwarzania.")
43 else: 25 return
44 await ctx.respond("nic nie gra") 26
27 queue = play_cog.get_queue(ctx.guild.id)
28
29 if not queue:
30 await ctx.respond("Kolejka jest pusta.")
31 return
32
33 # Stop the current song (this will trigger play_next in the after callback)
34 vc.stop()
35 await ctx.respond("Pominięto aktualną piosenkę.")
36
37 @commands.slash_command(name="kolejka", description="pokaż aktualną kolejkę")
38 async def queue_list(self, ctx):
39 """Show the current queue."""
40 play_cog = self.bot.get_cog('Play')
41 if not play_cog:
42 await ctx.respond("Błąd: Nie można znaleźć modułu odtwarzania.")
43 return
44
45 queue = play_cog.get_queue(ctx.guild.id)
46
47 if not queue:
48 await ctx.respond("Kolejka jest pusta.")
49 return
50
51 queue_list = []
52 for i, (title, _) in enumerate(queue[:10], 1): # Show up to 10 songs
53 queue_list.append(f"{i}. {title}")
54
55 if len(queue) > 10:
56 queue_list.append(f"...i {len(queue) - 10} więcej")
57
58 await ctx.respond("\n".join(["**Kolejka:**"] + queue_list))
45 59
46def setup(bot): 60def setup(bot):
47 bot.add_cog(Skip(bot)) \ No newline at end of file 61 bot.add_cog(Skip(bot)) \ No newline at end of file