From f63d6f24842b8193a17e2f9c678ceea4870aa322 Mon Sep 17 00:00:00 2001 From: Jellyfishsh Date: Sat, 19 Apr 2025 00:01:53 -0700 Subject: [PATCH] made changes to disconnect, began queue implementation --- bot.py | 94 +++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 34 deletions(-) diff --git a/bot.py b/bot.py index fccef41..d1f97e0 100644 --- a/bot.py +++ b/bot.py @@ -1,5 +1,6 @@ # Imports import os +from discord.webhook.async_ import interaction_message_response_params import requests import discord from discord.ext import commands @@ -47,6 +48,8 @@ bot = JellyfinBot() async def on_ready(): print(f"🪼 Logged in as {bot.user}") +# ----- Helpful Structures ----- # +guild_queue_dict = dict() # ----- Helper Functions ----- # @@ -82,15 +85,15 @@ async def make_request(url: str, params: dict, interaction: discord.Interaction) # Play a song - - # Play function # Plays a song with the given title in wherever the user is # start play ----- + @bot.tree.command(name="play", description="Play a song from Jellyfin") @app_commands.describe(title="Song title to play") async def play(interaction: discord.Interaction, title: str): + # Makes the reaction visible to everyone await interaction.response.defer() @@ -101,18 +104,16 @@ async def play(interaction: discord.Interaction, title: str): voice_status = await interaction.user.fetch_voice() user_channel = voice_status.channel voice_client = interaction.guild.voice_client # Enables the voice feature for the bot - # If its not connected to a voice channel, then connect to the channel that the caller is in - if voice_client is None: - voice_client = await user_channel.connect() - # If the caller is in a channel that its not already in, move there - elif voice_client.channel != user_channel: - await voice_client.move_to(user_channel) - voice_client = await user_channel.connect() - - #Pause the music that is currently playing + # First check if it is already in a voice channel + if voice_client.channel != user_channel: + voice_client.disconnect() + if voice_client.is_playing(): voice_client.stop() + + # Finally, connect + await user_channel.connect() except discord.errors.NotFound as e: print(f"Error: {e}") await interaction.followup.send("Nope! Not in a voice!") @@ -141,37 +142,56 @@ async def play(interaction: discord.Interaction, title: str): print(f"Found {len(data)} items matching `{title}`.") # Deconstructing the dict 'data' - song = data[0] - song_id = song.get('Id') - song_title = song.get('Name') - song_artist = song.get('AlbumArtist', ['Unknown Artist']) + query_song = data[0] + query_song_id = query_song.get('Id') + query_song_title = query_song.get('Name') + query_song_artist = query_song.get('AlbumArtist', ['Unknown Artist']) + + # First, we make a dictionary that has a list inside. + # Then, we get the name of the guild and put that in the dictionary + # then AFTER, we put the song inside the list that the guild points to + # on the play command, we add that song to the list - try: - #Setup for ffmpeg - audio_stream_url = f"{JELLYFIN_URL}/Audio/{song_id}/stream?static=True" - print(f"Stream URL: {audio_stream_url}") - headers_str = f"-headers \"X-Emby-Token: {JELLYFIN_API_KEY}\"" + # Check if the guild is not known guilds + if guild_queue_dict.get(interaction.guild_id) == None: + # add it to the playlist dict + guild_queue_dict[interaction.guild_id] = list() - # >>----- start PROBLEMS + guild_queue_dict[interaction.guild_id].append((query_song_id, query_song_title, query_song_artist)) - # Here is the problematic library. - # It might just be looking for Opus and not finding it - source = discord.FFmpegOpusAudio(audio_stream_url, - before_options=f'{headers_str} -reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', - options="-vn") + # If someone is already playing something, we just add to their queue. + # Probably a better way of piggybacking + if voice_client.is_playing(): + await interaction.followup.send(f"Queued: **{query_song_title}** by *{query_song_artist}*") + return - # end PROBLEMS -----<< + headers_str = f"-headers \"X-Emby-Token: {JELLYFIN_API_KEY}\"" + for song in guild_queue_dict[interaction.guild_id]: + try: + # Song information from tuple + song_id = song[0] + song_title = song[1] + song_artist = song[2] - # Play the audio file - voice_client.play(source, after=lambda e: print(f"Finished playing: {e}")) - await interaction.followup.send(f"🎶 Now playing: **{song_title}** by *{song_artist}*") + audio_stream_url = f"{JELLYFIN_URL}/Audio/{song_id}/stream?static=True" + source = discord.FFmpegOpusAudio(audio_stream_url, + before_options=f'{headers_str} -reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', + options="-vn") + # Play the grabbed audio file + voice_client.play(source, after=lambda e: print(f"Finished playing: {e}")) + await interaction.followup.send(f"Now playing: **{song_title}** by *{song_artist}*") + #Cheap way of holding the loop until its done + while voice_client.is_playing(): + continue - # If the library does not exist, we fail it - except discord.ClientException as e: - await interaction.followup.send("Unable to decode your song!") - print(f"Potential FFMPEG/OPUS decoding error: {e}") + # Then, we get rid of this queued item + guild_queue_dict[interaction.guild_id].pop(0) + + except discord.ClientException as e: + await interaction.followup.send("Unable to decode your song!") + print(f"Potential FFMPEG/OPUS decoding error: {e}") # end play ----- @@ -215,11 +235,17 @@ async def search(interaction: discord.Interaction, title: str): # Disconnects from the voice channel @bot.tree.command(name="disconnect", description="Disconnects from current voice channel!") async def disconnect(interaction: discord.Interaction): + # Checks if not in a voice channel if interaction.guild.voice_client == None: await interaction.followup.send("I am not in any voice channel!") return + # disconnects with a helpful message await interaction.guild.voice_client.disconnect() + await interaction.followup.send("Disconnected!") + return +# Queues +# Enqueue an item