feat: Add basic queueing functionality

This commit is contained in:
Roman 2023-06-06 17:01:17 -04:00
parent 8dd1283b62
commit 228a3df244
2 changed files with 65 additions and 6 deletions

View file

@ -14,9 +14,14 @@ defmodule Sergei.Consumer do
opt.(3, "url", "URL of the audio to play", []) opt.(3, "url", "URL of the audio to play", [])
] ]
@queue_opts [
opt.(3, "url", "URL of the audio to queue", required: true)
]
@slash_commands [ @slash_commands [
{"ping", "Pong", []}, {"ping", "Pong", []},
{"play", "Play a song or resume playback", @play_opts}, {"play", "Play a song or resume playback", @play_opts},
{"queue", "Queue a song to play next", @queue_opts},
{"pause", "Pause media playback", []}, {"pause", "Pause media playback", []},
{"stop", "Stop media playback and leave the voice channel", []}, {"stop", "Stop media playback and leave the voice channel", []},
{"song", "What song is currently playing?", []} {"song", "What song is currently playing?", []}
@ -127,6 +132,24 @@ defmodule Sergei.Consumer do
end end
end end
# /queue <url>
def do_command(%{
guild_id: guild_id,
data: %{name: "queue", options: [%{name: "url", value: url}]}
}) do
case Sergei.Player.queue(guild_id, url) do
:ok ->
{:ok, "Song queued."}
:not_playing ->
{:ok, "I'm not playing anything right now."}
{:error, err} ->
Logger.error("Failed to queue media: #{err}")
{:error, "This is embarassing..."}
end
end
# /pause # /pause
def do_command(%{guild_id: guild_id, data: %{name: "pause"}}) do def do_command(%{guild_id: guild_id, data: %{name: "pause"}}) do
case Sergei.Player.pause(guild_id) do case Sergei.Player.pause(guild_id) do

View file

@ -24,6 +24,11 @@ defmodule Sergei.Player do
GenServer.call(__MODULE__, {:play, guild_id, channel_id, url}) GenServer.call(__MODULE__, {:play, guild_id, channel_id, url})
end end
@spec queue(integer(), String.t()) :: :ok | :not_playing | {:error, String.t()}
def queue(guild_id, url) do
GenServer.call(__MODULE__, {:queue, guild_id, url})
end
@spec pause(integer()) :: :ok | :not_playing | {:error, String.t()} @spec pause(integer()) :: :ok | :not_playing | {:error, String.t()}
def pause(guild_id) do def pause(guild_id) do
GenServer.call(__MODULE__, {:pause, guild_id}) GenServer.call(__MODULE__, {:pause, guild_id})
@ -50,8 +55,21 @@ defmodule Sergei.Player do
state state
|> Enum.filter(fn {_id, %{paused: paused} = _state} -> not paused end) |> Enum.filter(fn {_id, %{paused: paused} = _state} -> not paused end)
|> Enum.filter(fn {guild_id, _data} -> not Voice.playing?(guild_id) end) |> Enum.filter(fn {guild_id, _data} -> not Voice.playing?(guild_id) end)
|> Enum.each(fn {guild_id, %{url: url}} = _state -> |> Enum.map(fn {guild_id, %{queue: queue} = state} ->
state =
case :queue.out(queue) do
{{:value, url}, queue} ->
%{state | url: url, queue: queue}
{:empty, _} ->
state
end
{guild_id, state}
end)
|> Enum.map(fn {guild_id, %{url: url}} = state ->
Voice.play(guild_id, url, :ytdl) Voice.play(guild_id, url, :ytdl)
state
end) end)
Process.send_after(self(), :check_repeat, @check_repeat_interval) Process.send_after(self(), :check_repeat, @check_repeat_interval)
@ -100,12 +118,28 @@ defmodule Sergei.Player do
state = state =
Map.put(state, guild_id, %{ Map.put(state, guild_id, %{
url: url, url: url,
paused: false paused: false,
queue: :queue.new()
}) })
{:reply, res, state} {:reply, res, state}
end end
@impl true
def handle_call({:queue, guild_id, url}, _from, state) do
%{queue: queue} = instance = Map.fetch!(state, guild_id)
{
:reply,
:ok,
Map.put(
state,
guild_id,
%{instance | queue: :queue.in(url, queue)}
)
}
end
@impl true @impl true
def handle_call({_, guild_id}, _from, state) when not is_map_key(state, guild_id) do def handle_call({_, guild_id}, _from, state) when not is_map_key(state, guild_id) do
{:reply, :not_playing, state} {:reply, :not_playing, state}
@ -113,13 +147,14 @@ defmodule Sergei.Player do
@impl true @impl true
def handle_call({:pause, guild_id}, _from, state) do def handle_call({:pause, guild_id}, _from, state) do
%{url: url} = Map.fetch!(state, guild_id) %{url: url, queue: queue} = Map.fetch!(state, guild_id)
Voice.pause(guild_id) Voice.pause(guild_id)
state = state =
Map.put(state, guild_id, %{ Map.put(state, guild_id, %{
url: url, url: url,
paused: true paused: true,
queue: queue
}) })
{:reply, :ok, state} {:reply, :ok, state}
@ -127,13 +162,14 @@ defmodule Sergei.Player do
@impl true @impl true
def handle_call({:resume, guild_id}, _from, state) do def handle_call({:resume, guild_id}, _from, state) do
%{url: url} = Map.fetch!(state, guild_id) %{url: url, queue: queue} = Map.fetch!(state, guild_id)
Voice.resume(guild_id) Voice.resume(guild_id)
state = state =
Map.put(state, guild_id, %{ Map.put(state, guild_id, %{
url: url, url: url,
paused: false paused: false,
queue: queue
}) })
{:reply, :ok, state} {:reply, :ok, state}