defmodule Thulani.Audio.Util do @spec stream_type(Nostrum.Voice.play_input()) :: {:ok, Nostrum.Voice.play_type()} | {:error, atom()} def stream_type(ref) when is_binary(ref) do cond do ytdl_link?(ref) -> {:ok, :ytdl} streamlink?(ref) -> {:ok, :stream} valid_url?(ref) -> {:ok, :url} true -> {:ok, :pipe} end end def stream_type(_ref) do {:error, :invalid} end @spec ytdl_link?(String.t()) :: boolean def ytdl_link?(s) when is_binary(s) do valid_url?(s) and ytdl_checks() |> any_check?(s) end def ytdl_link?(_s), do: false @spec streamlink?(String.t()) :: boolean def streamlink?(s) when is_binary(s) do valid_url?(s) and streamlink_checks() |> any_check?(s) end def streamlink?(_s), do: false def valid_url?(s), do: String.match?(s, ~r/^https:\/\/.*/i) def ytdl_checks, do: [&youtube_link?/1, &vimeo_link?/1] def streamlink_checks, do: [&twitch_link?/1] def youtube_link?(s), do: String.match?(s, youtube_regex()) def vimeo_link?(s), do: String.match?(s, vimeo_regex()) def twitch_link?(s), do: String.match?(s, twitch_regex()) def youtube_regex, do: ~r/^https?:\/\/(?:www\.)?(?:youtube\.com|youtu\.be)\/.*/i def vimeo_regex, do: ~r/^https?:\/\/(?:www\.)?(?:vimeo\.com)\/.*/i def twitch_regex, do: ~r/^https?:\/\/(?:www\.)?(?:twitch\.com)\/.*/i defp any_check?(checks, s) do checks |> Stream.map(fn check -> check.(s) end) |> Enum.reduce(false, fn acc, x -> acc or x end) end def wait_ready(guild, interval \\ 100, count \\ 10) do wait_for(guild, interval, count, &Nostrum.Voice.ready?/1) end def wait_playing(guild, interval \\ 200, count \\ 30) do wait_for(guild, interval, count, &Nostrum.Voice.playing?/1) end def wait_no_channel(guild, interval \\ 200, count \\ 30) do wait_for(guild, interval, count, fn guild -> no_channel?(guild) end) end def wait_disconnected(guild, interval \\ 200, count \\ 30) do wait_for(guild, interval, count, fn guild -> disconnected?(guild) end) end defp wait_for(guild, interval, count, check) do unless check.(guild) do [playing] = Stream.unfold(0, fn i -> Process.sleep(interval) playing_state = check.(guild) {playing_state, i + 1} end) |> Thulani.Util.Stream.take_until(&Function.identity/1) |> Stream.take(count) |> Enum.take(-1) playing else true end end def connected?(guild) do voice_state = Nostrum.Voice.get_voice(guild) !is_nil(voice_state) && !is_nil(voice_state.channel_id) end def disconnect(guild) do voice_state = Nostrum.Voice.get_voice(guild) Nostrum.Voice.leave_channel(guild) if not is_nil(voice_state) do Nostrum.Voice.Session.close_connection(voice_state.session_pid) end end def disconnected?(guild) do Nostrum.Voice.get_voice(guild) |> is_nil end def no_channel?(guild) do voice_state = Nostrum.Voice.get_voice(guild) !is_nil(voice_state) && is_nil(voice_state.channel_id) end end