Add Playlist function
This commit is contained in:
parent
91ce4aa22c
commit
f488c67c3c
1 changed files with 80 additions and 19 deletions
99
skullbot.py
99
skullbot.py
|
@ -148,6 +148,68 @@ async def hallo(ctx):
|
||||||
"""Simple hello command."""
|
"""Simple hello command."""
|
||||||
await ctx.send(f"Hello {ctx.author.mention}! 👋")
|
await ctx.send(f"Hello {ctx.author.mention}! 👋")
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# PLAYLIST QUEUE
|
||||||
|
# =====================
|
||||||
|
playlist_queue = [] # Liste from Dicts: { 'url':..., 'title':..., 'data':... }
|
||||||
|
is_playing = False
|
||||||
|
|
||||||
|
async def play_next(ctx):
|
||||||
|
global is_playing
|
||||||
|
if playlist_queue:
|
||||||
|
next_track = playlist_queue.pop(0)
|
||||||
|
player = await YTDLSource.from_url(next_track['url'], loop=bot.loop, stream=True)
|
||||||
|
ctx.voice_client.play(
|
||||||
|
player,
|
||||||
|
after=lambda e: asyncio.run_coroutine_threadsafe(play_next(ctx), bot.loop)
|
||||||
|
)
|
||||||
|
await ctx.send(f"🎶 Now playing: **{player.title}**")
|
||||||
|
is_playing = True
|
||||||
|
else:
|
||||||
|
is_playing = False
|
||||||
|
await ctx.send("✅ Playlist finished.")
|
||||||
|
|
||||||
|
@bot.command(name="add")
|
||||||
|
async def add(ctx, *, url):
|
||||||
|
"""
|
||||||
|
Fügt einen Song oder eine komplette YouTube-Playlist zur Warteschlange hinzu.
|
||||||
|
"""
|
||||||
|
# Playlist oder Einzelvideo abrufen
|
||||||
|
data = await asyncio.get_event_loop().run_in_executor(
|
||||||
|
None, lambda: ytdl.extract_info(url, download=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
added_titles = []
|
||||||
|
|
||||||
|
if 'entries' in data:
|
||||||
|
for entry in data['entries']:
|
||||||
|
playlist_queue.append({'url': entry['url'], 'title': entry['title'], 'data': entry})
|
||||||
|
added_titles.append(entry['title'])
|
||||||
|
else:
|
||||||
|
playlist_queue.append({'url': data['url'], 'title': data['title'], 'data': data})
|
||||||
|
added_titles.append(data['title'])
|
||||||
|
|
||||||
|
await ctx.send(f"✅ Added {len(added_titles)} track(s) to the playlist:\n" +
|
||||||
|
"\n".join(f"- {t}" for t in added_titles[:10]) +
|
||||||
|
(f"\n… +{len(added_titles)-10} more" if len(added_titles) > 10 else ""))
|
||||||
|
|
||||||
|
if not ctx.voice_client and ctx.author.voice:
|
||||||
|
await ctx.author.voice.channel.connect()
|
||||||
|
|
||||||
|
if ctx.voice_client and not ctx.voice_client.is_playing():
|
||||||
|
await play_next(ctx)
|
||||||
|
|
||||||
|
@bot.command(name="playlist")
|
||||||
|
async def playlist_cmd(ctx):
|
||||||
|
"""
|
||||||
|
Zeigt die aktuelle Playlist/Warteschlange an.
|
||||||
|
"""
|
||||||
|
if not playlist_queue:
|
||||||
|
await ctx.send("🎵 Die Playlist ist leer.")
|
||||||
|
else:
|
||||||
|
text = "\n".join([f"{idx+1}. {track['title']}" for idx, track in enumerate(playlist_queue[:20])])
|
||||||
|
await ctx.send(f"🎵 **Aktuelle Playlist:**\n{text}")
|
||||||
|
|
||||||
# =====================
|
# =====================
|
||||||
# YTDL / FFMPEG SETUP
|
# YTDL / FFMPEG SETUP
|
||||||
# =====================
|
# =====================
|
||||||
|
@ -165,10 +227,6 @@ ffmpeg_options = {
|
||||||
ytdl = youtube_dl.YoutubeDL(ytdl_format_options)
|
ytdl = youtube_dl.YoutubeDL(ytdl_format_options)
|
||||||
|
|
||||||
class YTDLSource(discord.PCMVolumeTransformer):
|
class YTDLSource(discord.PCMVolumeTransformer):
|
||||||
"""
|
|
||||||
Handles downloading and streaming audio from YouTube.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, source, *, data, volume=0.5):
|
def __init__(self, source, *, data, volume=0.5):
|
||||||
super().__init__(source, volume)
|
super().__init__(source, volume)
|
||||||
self.data = data
|
self.data = data
|
||||||
|
@ -177,13 +235,19 @@ class YTDLSource(discord.PCMVolumeTransformer):
|
||||||
@classmethod
|
@classmethod
|
||||||
async def from_url(cls, url, *, loop=None, stream=False):
|
async def from_url(cls, url, *, loop=None, stream=False):
|
||||||
loop = loop or asyncio.get_event_loop()
|
loop = loop or asyncio.get_event_loop()
|
||||||
data = await loop.run_in_executor(
|
try:
|
||||||
None, lambda: ytdl.extract_info(url, download=not stream)
|
data = await loop.run_in_executor(
|
||||||
)
|
None, lambda: ytdl.extract_info(url, download=not stream)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
return None, e
|
||||||
|
|
||||||
if "entries" in data:
|
if "entries" in data:
|
||||||
data = data["entries"][0]
|
data = data["entries"][0]
|
||||||
|
|
||||||
filename = data["url"] if stream else ytdl.prepare_filename(data)
|
filename = data["url"] if stream else ytdl.prepare_filename(data)
|
||||||
return cls(discord.FFmpegPCMAudio(filename, **ffmpeg_options), data=data)
|
return cls(discord.FFmpegPCMAudio(filename, **ffmpeg_options), data=data), None
|
||||||
|
|
||||||
|
|
||||||
# =====================
|
# =====================
|
||||||
# VOICE / MUSIC COMMANDS
|
# VOICE / MUSIC COMMANDS
|
||||||
|
@ -208,24 +272,23 @@ async def leave(ctx):
|
||||||
|
|
||||||
@bot.command(name="play")
|
@bot.command(name="play")
|
||||||
async def play(ctx, *, url):
|
async def play(ctx, *, url):
|
||||||
"""
|
"""Streams Audio von einer YouTube URL"""
|
||||||
Streams audio from a given YouTube URL to the connected voice channel.
|
|
||||||
Adds interactive buttons to control playback.
|
|
||||||
"""
|
|
||||||
# Connect to voice channel if not already connected
|
|
||||||
if not ctx.voice_client:
|
if not ctx.voice_client:
|
||||||
if ctx.author.voice:
|
if ctx.author.voice:
|
||||||
await ctx.author.voice.channel.connect()
|
await ctx.author.voice.channel.connect()
|
||||||
else:
|
else:
|
||||||
await ctx.send("You are not in a voice channel!")
|
await ctx.send("Du bist in keinem Sprachkanal!")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Start streaming
|
player, error = await YTDLSource.from_url(url, loop=bot.loop, stream=True)
|
||||||
player = await YTDLSource.from_url(url, loop=bot.loop, stream=True)
|
if error or not player:
|
||||||
|
await ctx.send("⚠️ Konnte den Titel nicht abspielen. "
|
||||||
|
"YouTube Restrictions blockieren möglicherweise den Zugriff.")
|
||||||
|
return
|
||||||
|
|
||||||
ctx.voice_client.stop()
|
ctx.voice_client.stop()
|
||||||
ctx.voice_client.play(player)
|
ctx.voice_client.play(player)
|
||||||
|
|
||||||
# Create interactive buttons
|
|
||||||
stop_button = Button(label="Stop", style=discord.ButtonStyle.red)
|
stop_button = Button(label="Stop", style=discord.ButtonStyle.red)
|
||||||
pause_button = Button(label="Pause", style=discord.ButtonStyle.gray)
|
pause_button = Button(label="Pause", style=discord.ButtonStyle.gray)
|
||||||
resume_button = Button(label="Resume", style=discord.ButtonStyle.green)
|
resume_button = Button(label="Resume", style=discord.ButtonStyle.green)
|
||||||
|
@ -245,12 +308,10 @@ async def play(ctx, *, url):
|
||||||
ctx.voice_client.resume()
|
ctx.voice_client.resume()
|
||||||
await interaction.response.defer()
|
await interaction.response.defer()
|
||||||
|
|
||||||
# Attach callbacks
|
|
||||||
stop_button.callback = stop_callback
|
stop_button.callback = stop_callback
|
||||||
pause_button.callback = pause_callback
|
pause_button.callback = pause_callback
|
||||||
resume_button.callback = resume_callback
|
resume_button.callback = resume_callback
|
||||||
|
|
||||||
# Build and send view
|
|
||||||
view = View()
|
view = View()
|
||||||
view.add_item(stop_button)
|
view.add_item(stop_button)
|
||||||
view.add_item(pause_button)
|
view.add_item(pause_button)
|
||||||
|
|
Loading…
Add table
Reference in a new issue