aboutsummaryrefslogtreecommitdiffstats
path: root/cogs
diff options
context:
space:
mode:
Diffstat (limited to 'cogs')
-rw-r--r--cogs/music/play.py89
-rw-r--r--cogs/music/play_compressed.py101
-rw-r--r--cogs/music/play_utils.py117
3 files changed, 135 insertions, 172 deletions
diff --git a/cogs/music/play.py b/cogs/music/play.py
index f18afa8..7066ae8 100644
--- a/cogs/music/play.py
+++ b/cogs/music/play.py
@@ -1,83 +1,30 @@
1import logging
2import discord 1import discord
3import yt_dlp 2from . import play_utils
4from discord.ext import commands 3from discord.ext import commands
5from youtubesearchpython import VideosSearch 4import logging
6 5
7logger = logging.getLogger(__name__) 6logger = logging.getLogger(__name__)
8 7
9# Suppress noise about console usage from yt-dlp
10yt_dlp.utils.bug_reports_message = lambda: ''
11
12
13class Play(commands.Cog): 8class Play(commands.Cog):
14 def __init__(self, bot): 9 def __init__(self, bot):
15 self.bot = bot 10 self.bot = bot
16 self.ydl_opts = {
17 'extract_audio': True,
18 'noplaylist': True,
19 'quiet': True,
20 'default_search': 'auto',
21 }
22
23 async def play_audio(self, ctx, vc, url, title):
24 try:
25 with yt_dlp.YoutubeDL(self.ydl_opts) as ydl:
26 info = await self.bot.loop.run_in_executor(
27 None,
28 lambda: ydl.extract_info(url, download=False)
29 )
30 if 'entries' in info:
31 info = info['entries'][0]
32 11
33 url2 = info['url'] 12 @discord.slash_command(name="grajcuj", description="grajcujmy wszyscy!")
34 13 async def play(self, ctx: discord.ApplicationContext, query: str = discord.Option(str, "co zagrać??", required=True)):
35 ffmpeg_options = {
36 'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5',
37 'options': '-vn'
38 }
39
40 source = discord.FFmpegPCMAudio(url2, **ffmpeg_options)
41 vc.play(source)
42 await ctx.followup.send(f"gra: {title}")
43 logger.info(f"Playing: {title}")
44 except Exception as e:
45 await ctx.followup.send(f"Wystąpił błąd podczas odtwarzania: {str(e)}")
46 logger.error(f"Error playing audio: {e}", exc_info=True)
47
48 @commands.slash_command(name="grajcuj", description="grajcuj melodyje")
49 async def play(self, ctx, *, search: str):
50 await ctx.defer() 14 await ctx.defer()
51 15 url, title = await play_utils.PlayUtils(self.bot).get_url(ctx, query)
52 if not (ctx.author.voice and ctx.author.voice.channel): 16 logger.info(f"Found video: {title}")
53 await ctx.followup.send("nie jesteś w kanale głosowym") 17 filename = await play_utils.PlayUtils(self.bot).download(ctx, url)
54 logger.info("User is not in a voice channel") 18 logger.info(f"Downloaded file: {filename}")
55 return 19 await play_utils.PlayUtils(self.bot).play(ctx, filename, title)
56 20
57 channel = ctx.author.voice.channel 21 @discord.slash_command(name="grajcuj_zkompresowane", description="grajcujmy wszyscy w wysokiej jakosci!")
58 vc = ctx.voice_client or await channel.connect() 22 async def play_compressed(self, ctx: discord.ApplicationContext, url: str = discord.Option(str, "co zagrać??", required=True)):
59 23 await ctx.defer()
60 if vc.channel != channel: 24 url, title = await play_utils.PlayUtils(self.bot).get_url(ctx, url)
61 await vc.move_to(channel) 25 filename = await play_utils.PlayUtils(self.bot).download(ctx, url)
62 26 filename = await play_utils.PlayUtils(self.bot).compress(ctx, filename)
63 # Search for the video 27 await play_utils.PlayUtils(self.bot).play(ctx, filename, title)
64 videos_search = VideosSearch(search, limit=1)
65 result = videos_search.result()
66
67 if not result['result']:
68 await ctx.followup.send("nie znaleziono wideo dla twojego wyszukiwania.", ephemeral=True)
69 logger.info(f"Could not find a video for search: {search}")
70 return
71
72 video = result['result'][0]
73 title = video['title']
74 url = video['link']
75
76 # Play the audio directly
77 if vc.is_playing() or vc.is_paused():
78 vc.stop()
79 await self.play_audio(ctx, vc, url, title)
80
81 28
82def setup(bot): 29def setup(bot):
83 bot.add_cog(Play(bot)) 30 bot.add_cog(Play(bot)) \ No newline at end of file
diff --git a/cogs/music/play_compressed.py b/cogs/music/play_compressed.py
deleted file mode 100644
index 2bae269..0000000
--- a/cogs/music/play_compressed.py
+++ /dev/null
@@ -1,101 +0,0 @@
1import asyncio
2import logging
3import os
4import subprocess
5
6import discord
7from discord.ext import commands
8from youtubesearchpython import VideosSearch
9
10logger = logging.getLogger(__name__)
11
12
13class GrajcujZkompresowane(commands.Cog):
14 def __init__(self, bot):
15 self.bot = bot
16
17 @staticmethod
18 def prepend_title_to_file(file_path, title):
19 try:
20 with open(file_path, 'rb') as original:
21 data = original.read()
22 with open(file_path, 'wb') as modified:
23 modified.write(f"{title}\n".encode('utf-8'))
24 modified.write(data)
25 except Exception as e:
26 logger.error(f"Failed to prepend title: {e}", exc_info=True)
27
28 @staticmethod
29 def compress_audio(input_path):
30 base, ext = os.path.splitext(input_path)
31 output_path = f"{base}_compressed.opus"
32 ffmpeg_command = f"ffmpeg -i {input_path} -ac 1 -b:a 500 -acodec libopus {output_path}"
33 subprocess.run(ffmpeg_command, check=True, shell=True)
34 logger.info(f"Audio compressed to {output_path}")
35 return output_path
36
37 @commands.slash_command(name="grajcuj_zkompresowane", description="grajcuj zkompresowane melodyje")
38 async def grajcuj_zkompresowane(self, ctx, *, search: str):
39 await ctx.defer()
40 if not (ctx.author.voice and ctx.author.voice.channel):
41 await ctx.followup.send("nie jesteś w kanale głosowym")
42 logger.info("User is not in a voice channel")
43 return
44 channel = ctx.author.voice.channel
45 if ctx.voice_client is not None:
46 vc = ctx.voice_client
47 logger.info(f"User is already in a voice channel, moving to new {channel}")
48 await vc.move_to(channel)
49 else:
50 vc = await channel.connect()
51 bot_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
52 music_dir = os.path.join(bot_dir, "temp", "music")
53 if not os.path.exists(music_dir):
54 os.makedirs(music_dir)
55 videos_search = VideosSearch(search, limit=1)
56 result = videos_search.result()
57 if not result['result']:
58 await ctx.followup.send("Could not find a video for your search.", ephemeral=True)
59 return
60 video = result['result'][0]
61 video_id = video['id']
62 title = video['title']
63 url = video['link']
64 filename = f"{video_id}.mp3"
65 file_path = os.path.join(music_dir, filename)
66 compressed_file_path = os.path.join(music_dir, f"{video_id}_compressed.opus")
67 if os.path.exists(compressed_file_path):
68 found_file = compressed_file_path
69 found_title = title
70 else:
71 if not os.path.exists(file_path):
72 cmd = [
73 "yt-dlp",
74 "-x",
75 "--audio-format", "mp3",
76 "-o", file_path,
77 url
78 ]
79 process = await asyncio.create_subprocess_exec(*cmd)
80 await process.communicate()
81 self.prepend_title_to_file(file_path, title)
82 if not os.path.exists(compressed_file_path):
83 try:
84 compressed_file_path = self.compress_audio(file_path)
85 except subprocess.CalledProcessError as e:
86 await ctx.followup.send(f"skompresowanie nie powiodło się: {str(e)}", ephemeral=True)
87 logger.error(f"Failed to compress audio: {e}", exc_info=True)
88 return
89 found_file = compressed_file_path
90 found_title = title
91 title = found_title
92
93 if vc.is_playing() or vc.is_paused():
94 vc.stop()
95 vc.play(discord.FFmpegPCMAudio(found_file))
96 await ctx.followup.send(f"gra (zkompresowane): {title}")
97 logger.info(f"Playing audio: {title}")
98
99
100def setup(bot):
101 bot.add_cog(GrajcujZkompresowane(bot))
diff --git a/cogs/music/play_utils.py b/cogs/music/play_utils.py
new file mode 100644
index 0000000..1e46594
--- /dev/null
+++ b/cogs/music/play_utils.py
@@ -0,0 +1,117 @@
1import logging
2from youtubesearchpython import VideosSearch
3import yt_dlp
4import discord
5import asyncio
6import os
7
8logger = logging.getLogger(__name__)
9
10class PlayUtils:
11 def __init__(self, bot):
12 self.bot = bot
13
14 async def download(self, ctx, url):
15
16 # Create temp directory if it doesn't exist
17 temp_dir = os.path.join('cogs', 'temp', 'music')
18 os.makedirs(temp_dir, exist_ok=True)
19
20 ytdl_opts = {
21 "format": "bestaudio/best",
22 "outtmpl": os.path.join(temp_dir, "%(title)s.%(ext)s"),
23 "postprocessors": [
24 {
25 "key": "FFmpegExtractAudio",
26 "preferredcodec": "mp3",
27 "preferredquality": "192",
28 }
29 ],
30 }
31
32 try:
33 with yt_dlp.YoutubeDL(ytdl_opts) as ydl:
34 info_dict = ydl.extract_info(url, download=True)
35 # Get the base filename without extension
36 base_filename = os.path.splitext(ydl.prepare_filename(info_dict))[0]
37 # Add .mp3 extension since we're extracting audio
38 filename = f"{base_filename}.mp3"
39 logger.info(f"Downloaded file: {filename}")
40 return filename
41 except Exception as e:
42 logger.error(f"Error downloading video: {e}")
43 await ctx.respond("eror pobirajync wideło", ephemeral=True)
44
45 async def get_url(self, ctx, query):
46 search = VideosSearch(query, limit=1)
47 if search.result()["result"]:
48 title = search.result()["result"][0]["title"]
49 url = search.result()["result"][0]["link"]
50 logger.info(f"Found video: {title}")
51 return url, title
52 else:
53 logger.warning("No results found for the query.")
54 await ctx.respond("nic ni ma", ephemeral=True)
55 return None
56
57 async def compress(self, ctx, filename):
58 if not filename:
59 await ctx.respond("dzie wideło", ephemeral=True)
60 return
61
62 ffmpeg_command = f"ffmpeg -i {filename} -ac 1 -b:a 500 -acodec libopus {filename}"
63 try:
64 process = await asyncio.create_subprocess_shell(
65 ffmpeg_command,
66 stdout=asyncio.subprocess.PIPE,
67 stderr=asyncio.subprocess.PIPE
68 )
69 stdout, stderr = await process.communicate()
70 if process.returncode != 0:
71 logger.error(f"FFmpeg error: {stderr.decode()}")
72 await ctx.respond("eror kompresujync pilk", ephemeral=True)
73 else:
74 logger.info(f"Compressed file: {filename}")
75 except Exception as e:
76 logger.error(f"Error compressing file: {e}")
77 await ctx.respond("eror kompresujync pilk", ephemeral=True)
78
79 async def play(self, ctx, filename, title):
80 # Check if user is in a voice channel
81 if not ctx.author.voice or not ctx.author.voice.channel:
82 await ctx.respond("Musisz być na kanale głosowym, żebym mógł dołączyć!", ephemeral=True)
83 return
84
85 # Get or create voice client
86 vc = ctx.voice_client
87 target_channel = ctx.author.voice.channel
88
89 try:
90 # Connect to voice channel if not connected
91 if vc is None:
92 vc = await target_channel.connect()
93 logger.info(f"Connected to voice channel: {target_channel.name}")
94 # Move to user's channel if in a different one
95 elif vc.channel != target_channel:
96 await vc.move_to(target_channel)
97 logger.info(f"Moved to voice channel: {target_channel.name}")
98
99 if not filename or not os.path.exists(filename):
100 await ctx.respond("Nie znaleziono pliku do odtworzenia", ephemeral=True)
101 logger.warning(f"No file to play, filename: {filename}")
102 return
103
104 # Stop any currently playing audio
105 if vc.is_playing() or vc.is_paused():
106 vc.stop()
107
108 # Play the audio
109 source = discord.FFmpegPCMAudio(source=filename)
110 await vc.play(source)
111
112 logger.info(f"Playing file: {title}")
113 await ctx.respond(f"grom: {title}")
114
115 except Exception as e:
116 logger.error(f"Error in play: {str(e)}", exc_info=True)
117 await ctx.respond(f"eror: {str(e)}", ephemeral=True) \ No newline at end of file