From 72419a0485513abcc933ec2c65aacd44d21cba49 Mon Sep 17 00:00:00 2001 From: cloudwithax Date: Sat, 15 Apr 2023 18:10:00 -0400 Subject: [PATCH] finally im finished with this stupid thing --- libs/filters.lua | 276 ++++++++++++++++++++++++++++++++++++++++++ libs/node.lua | 57 +++++++-- libs/player.lua | 201 +++++++++++++++++++----------- libs/playlist.lua | 14 ++- libs/routeplanner.lua | 0 libs/track.lua | 22 +++- libs/utils.lua | 7 +- package.lua | 27 ++--- 8 files changed, 510 insertions(+), 94 deletions(-) create mode 100644 libs/filters.lua delete mode 100644 libs/routeplanner.lua diff --git a/libs/filters.lua b/libs/filters.lua new file mode 100644 index 0000000..9d785a3 --- /dev/null +++ b/libs/filters.lua @@ -0,0 +1,276 @@ +function equalizer(_bands) + local bands = { + { 0, 0.0 }, + { 1, 0.0 }, + { 2, 0.0 }, + { 3, 0.0 }, + { 4, 0.0 }, + { 5, 0.0 }, + { 6, 0.0 }, + { 7, 0.0 }, + { 8, 0.0 }, + { 9, 0.0 }, + { 10, 0.0 }, + { 11, 0.0 }, + { 12, 0.0 }, + { 13, 0.0 }, + { 14, 0.0 } + } + + if _bands then + if not #_bands == 15 then return false, "There must be 15 bands within your equalizer." end + bands = _bands + end + + local payload = { + ['equalizer'] = bands + } + + return payload +end + +function timescale(_speed, _pitch, _rate) + local speed = 1.0 + local pitch = 1.0 + local rate = 1.0 + + if _speed then + speed = -_speed + end + + if _pitch then + pitch = _pitch + end + + if _rate then + rate = _rate + end + + local payload = { + ['timescale'] = { + ['speed'] = speed, + ['pitch'] = pitch, + ['rate'] = rate + + } + } + + return payload +end + +function karaoke(_level, _mono_level, _filter_band, _filter_width) + local level = 1.0 + local mono_level = 1.0 + local filter_band = 220.0 + local filter_width = 100.0 + + + if _level then + level = _level + end + + if _mono_level then + mono_level = _mono_level + end + + if _filter_band then + filter_band = _filter_band + end + + if _filter_width then + filter_width = _filter_width + end + + local payload = { + ['karaoke'] = { + ['level'] = level, + ['mono_level'] = mono_level, + ['filter_band'] = filter_band, + ['filter_width'] = filter_width + } + } + + return payload +end + +function tremolo(_frequency, _depth) + local frequency = 2.0 + local depth = 0.5 + + if _frequency then + frequency = frequency + end + + if depth then + depth = _depth + end + + local payload = { + ['tremolo'] = { + ['frequency'] = frequency, + ['depth'] = depth + } + } + + return payload +end + +function vibrato(_frequency, _depth) + local frequency = 2.0 + local depth = 0.5 + + if _frequency then + frequency = frequency + end + + if depth then + depth = _depth + end + + local payload = { + ['vibrato'] = { + ['frequency'] = frequency, + ['depth'] = depth + } + } + + return payload +end + +function rotation(_hertz) + local hertz = 5 + + if _hertz then + hertz = _hertz + end + + local payload = { + ['rotation'] = { + ['rotationHz'] = hertz + } + } + + return payload +end + +function channel_mix(_left_to_left, _right_to_right, _left_to_right, _right_to_left) + local left_to_left = 1 + local right_to_right = 1 + local left_to_right = 0 + local right_to_left = 0 + + if _left_to_left then + left_to_left = _left_to_left + end + + if _right_to_right then + right_to_right = _right_to_right + end + + if _left_to_right then + left_to_right = _left_to_right + end + + if _right_to_left then + right_to_left = _right_to_left + end + + local payload = { + ['channelMix'] = { + ['leftToLeft'] = left_to_left, + ['leftToRight'] = left_to_right, + ['rightToLeft'] = right_to_left, + ['rightToRight'] = right_to_right + + } + } + + return payload +end + +function distortion(_sin_offset, _sin_scale, _cos_offset, _cos_scale, _tan_offset, _tan_scale, _offset, _scale) + local sin_offset = 0 + local sin_scale = 1 + local cos_offset = 0 + local cos_scale = 1 + local tan_offset = 0 + local tan_scale = 1 + local offset = 0 + local scale = 1 + + if _sin_offset then + sin_offset = _sin_offset + end + + if _sin_scale then + sin_scale = _sin_scale + end + + if _cos_offset then + cos_offset = _cos_offset + end + + if _cos_scale then + cos_scale = _cos_scale + end + + if _tan_offset then + tan_offset = _tan_offset + end + + if _tan_scale then + tan_scale = _tan_scale + end + + if _offset then + offset = _offset + end + + if _scale then + scale = _scale + end + + local payload = { + ['distortion'] = { + ['sinOffset'] = sin_offset, + ['sinScale'] = sin_scale, + ['cosOffset'] = cos_offset, + ['cosScale'] = cos_scale, + ['tanOffset'] = tan_offset, + ['tanScale'] = tan_scale, + ['offset'] = offset, + ['scale'] = scale + + } + } + + return payload +end + +function low_pass(_smoothing) + local smoothing = 20 + + if _smoothing then + smoothing = _smoothing + end + + local payload = { + ['lowPass'] = { + ['smoothing'] = smoothing + } + } + + return payload +end + +return { + equalizer = equalizer, + timescale = timescale, + karaoke = karaoke, + tremolo = tremolo, + vibrato = vibrato, + rotation = rotation, + channel_mix = channel_mix, + distortion = distortion, + low_pass = low_pass +} diff --git a/libs/node.lua b/libs/node.lua index ec17569..33868ae 100644 --- a/libs/node.lua +++ b/libs/node.lua @@ -7,7 +7,6 @@ local utils = require('utils') local enums = require('enums') local interp = utils.interp local split = utils.split -local dump = utils.dump local url = require('url') local SearchType = enums.SearchType local Track = require('track') @@ -17,7 +16,7 @@ local Player = require('player') local Emitter = discordia.Emitter local class = discordia.class -local Node, get = class('Node', Emitter) +local Node = class('Node', Emitter) local format = string.format @@ -65,6 +64,34 @@ function Node:__init(client, options) self._players = {} + self._client:on('raw', function(data) + data = json.decode(data) + + if data.t == 'VOICE_SERVER_UPDATE' then + data = data.d + local guild = self._client:getGuild(data.guild_id) + if not guild then return end + + local user = guild.me or guild:getMember(self._client.user.id) + if not user then return end + + local state = guild._voice_states[user.id] + if not state then return end + + local voice_data = { + ['voice'] = { + ['token'] = data.token, + ['endpoint'] = data.endpoint, + ['sessionId'] = state.session_id + } + } + + local player_endpoint = string.format("sessions/%s/players", self._session_id) + + self:_send('PATCH', player_endpoint, guild.id, voice_data, nil, true) + end + end) + self:connect() end @@ -116,6 +143,8 @@ function Node:disconnect(forced) if not self._connected then return end self._connected = false self._res, self._read = nil, nil + self:emit('killed') + self:removeAllListeners('event') if not forced then self:_reconnect() end end @@ -124,13 +153,11 @@ function Node:_send(method, path, _guild_id, _data, _query, include_version) local guild_id = _guild_id or "" local query = _query or "" - local data = _data or "" + local data = "" local version = "" - print(dump(_guild_id)) - if include_version then - version = "/v" .. self._version .. "/" + version = "v" .. self._version .. "/" end if _guild_id then @@ -141,6 +168,11 @@ function Node:_send(method, path, _guild_id, _data, _query, include_version) query = "?" .. _query end + if _data then + data = json.encode(_data) + table.insert(self._headers, { "Content-Type", "application/json" }) + end + local uri = format('%s%s%s%s%s', self._rest_uri, version, path, guild_id, query) local res, body = http.request(method, uri, self._headers, data) @@ -209,13 +241,22 @@ function Node:create_player(vc) if not guild then return false, 'Could not find guild' end if self._players[guild.id] then return self._players[guild.id] end - local success, err = vc:join() + + + local success, err = self._client._shards[vc.guild.shardId]:updateVoice(vc.guild.id, vc.id) if not success then return nil, err end local player = Player(self, vc) self._players[guild.id] = player return player - +end + +function Node:get_player(guild_id) + if self._players[guild_id] then + return self._players[guild_id] + else + return false, "Player not found" + end end return Node diff --git a/libs/player.lua b/libs/player.lua index a89b3d1..ad4ea13 100644 --- a/libs/player.lua +++ b/libs/player.lua @@ -1,128 +1,191 @@ local discordia = require('discordia') local Emitter = discordia.Emitter local class = discordia.class +local json = require('json') local Player, get = class('FlarePlayer', Emitter) -local utils = require('utils') local format = string.format local function bind(t, k) - return function(...) return t[k](t, ...) end + return function(...) return t[k](t, ...) end end function Player:__init(node, channel) - Emitter.__init(self) - self._node = node - self._client = node._client + Emitter.__init(self) + self._node = node + self._client = node._client - self._channel = channel - self._guild = channel.guild - + self._channel = channel + self._guild = channel.guild - self._playing = false - self._paused = false - self._volume = 100 - self._current = nil - self._track_pos = nil - self._last_update = nil - self._player_endpoint_url = string.format("sessions/%s/players", self._node._session_id) + self._playing = false + self._paused = false + self._volume = 100 + self._current = nil + self._track_pos = nil + self._last_update = nil + self._session_id = nil + self._filter = nil - self._node:on('event', bind(self, '_onEvent')) - self._node:on('killed', bind(self, '_onNodeKilled')) + self._player_endpoint_url = string.format("sessions/%s/players", self._node._session_id) + + self._node:on('event', bind(self, '_onEvent')) + -- self._node:on('killed', bind(self, '_onNodeKilled')) end function Player:_onEvent(data) - if data.guildId ~= self._guild.id then return end - if data.op == 'playerUpdate' then - if not self._playing then return false end - self._track_pos = data.state.position - self._last_update = data.state.time - elseif data.type == 'TrackEndEvent' then - self:_clearTrack() - self:emit('end', data.reason:lower()) - elseif data.type == 'TrackExceptionEvent' then - self:_clearTrack() - self:emit('end', 'error', data.error) - elseif data.type == 'TrackStuckEvent' then - self:stop() - self:emit('end', data) - else - self:emit('warn', format('Unknown Event %s', data.type)) - end + if data.guildId ~= self._guild.id then return end + if data.op == 'playerUpdate' then + if not self._playing then return false end + self._track_pos = data.state.position + self._last_update = data.state.time + elseif data.type == 'TrackEndEvent' then + self:_clearTrack() + self:emit('end', data.reason:lower()) + elseif data.type == 'TrackExceptionEvent' then + self:_clearTrack() + self:emit('end', 'error', data.error) + elseif data.type == 'TrackStuckEvent' then + self:stop() + self:emit('end', data) + else + self:emit('warn', format('Unknown Event %s', data.type)) end +end + +function Player:_clearTrack() + self._playing = false + self._current = nil + self._last_update = nil + self._track_pos = nil +end function Player:play(track, start_time, end_time, _ignore_if_playing) - assert(type(track) == "table", "Invalid track provided") + assert(type(track) == "table", "Invalid track provided") - local data = { - {"encodedTrack", track._track_id}, - {"position", tostring(start_time)}, - {"endTime", nil} + local data = { + ["encodedTrack"] = track._track_id, + ["position"] = start_time or 0, + ["endTime"] = json.null + } - } + if end_time and end_time > 0 then + data["endTime"] = tostring(end_time) + end - if end_time and end_time > 0 then - data["endTime"] = tostring(end_time) - end + local ignore_if_playing = _ignore_if_playing or false - local ignore_if_playing = _ignore_if_playing or false + self._node:_send("PATCH", self._player_endpoint_url, self._guild.id, data, + string.format("noReplace=%s", ignore_if_playing), true) - self._node:_send('PATCH', self._player_endpoint_url, self._guild.id, data, string.format("noReplace=%s", ignore_if_playing)) - - self._current = track + self._current = track end function Player:stop() - local data = { - {"encodedTrack", nil}, + local data = { + ['encodedTrack'] = json.null - } - self._node:_send('PATCH', self._player_endpoint_url, self._guild.id, data) + } - self._current = nil - + self._node:_send('PATCH', self._player_endpoint_url, self._guild.id, data, nil, true) + + self._current = nil end function Player:destroy() - - self._node:_send('DELETE', self._player_endpoint_url, self._guild.id) + self._node:_send('DELETE', self._player_endpoint_url, self._guild.id, nil, nil, true) + local success, err = self._client._shards[self._guild.shardId]:updateVoice(self._guild.id, nil) + if not success then return nil, err end + self:removeAllListeners('end') + self:removeAllListeners('warn') self._node._players[self._guild.id] = nil - end function Player:set_pause(paused) + local data = { + ['paused'] = paused + } - local data = {{"paused", paused}} - - self._node:_send('PATCH', self._player_endpoint_url, self._guild.id, data) + self._node:_send('PATCH', self._player_endpoint_url, self._guild.id, data, nil, true) self._paused = paused - end function Player:seek(ms) + local data = { + ['position'] = ms + } - local data = {{"position", ms}} - - self._node:_send('PATCH', self._player_endpoint_url, self._guild.id, data) - + self._node:_send('PATCH', self._player_endpoint_url, self._guild.id, data, nil, true) end function Player:set_volume(volume) + local data = { + ['volume'] = volume + } - local data = {{"volume", volume}} - - self._node:_send('PATCH', self._player_endpoint_url, self._guild.id, data) + self._node:_send('PATCH', self._player_endpoint_url, self._guild.id, data, nil, true) self._volume = volume - end function Player:get_tracks(query, search_type) return self._node:get_tracks(query, search_type) - end +function Player:set_filter(filter) + assert(type(filter) == "table", "Invalid filter provided") + + local data = { + ['filters'] = filter + } + + self._node:_send('PATCH', self._player_endpoint_url, self._guild.id, data, nil, true) + + self._filter = filter +end + +function Player:remove_filter() + if not self._filter then return false, "No filter is applied." end + + -- this is the kind of bullshit i have to do to get "{}" + -- in the payload. yes i know, lua is garbage. + local empty = {} + + local meta = { + __jsontype = "object" + } + + setmetatable(empty, meta) + + local data = { + ['filters'] = empty + } + + self._node:_send('PATCH', self._player_endpoint_url, self._guild.id, data, nil, true) + + self._filter = nil +end + +function get.playing(self) + return self._playing +end + +function get.paused(self) + return self._paused +end + +function get.volume(self) + return self._volume +end + +function get.current(self) + return self._current +end + +function get.filter(self) + return self._filter +end return Player diff --git a/libs/playlist.lua b/libs/playlist.lua index b4e695d..43d35c8 100644 --- a/libs/playlist.lua +++ b/libs/playlist.lua @@ -1,7 +1,7 @@ local discordia = require('discordia') local class = discordia.class -local Playlist = class('Playlist') +local Playlist, get = class('Playlist') function Playlist:__init(data, tracks) local info = data.playlistInfo @@ -16,4 +16,16 @@ function Playlist:__init(data, tracks) self._track_count = #self._tracks end +function get.name(self) + return self._name +end + +function get.tracks(self) + return self._tracks +end + +function get.track_count(self) + return self._track_count +end + return Playlist diff --git a/libs/routeplanner.lua b/libs/routeplanner.lua deleted file mode 100644 index e69de29..0000000 diff --git a/libs/track.lua b/libs/track.lua index 5aad8f6..b2d7f8a 100644 --- a/libs/track.lua +++ b/libs/track.lua @@ -1,7 +1,7 @@ local discordia = require('discordia') local class = discordia.class -local Track = class('Track') +local Track, get = class('Track') function Track:__init(data) local info = data.info @@ -17,4 +17,24 @@ function Track:__init(data) self._is_seekable = info.isSeekable end +function get.title(self) + return self._track +end + +function get.uri(self) + return self._uri +end + +function get.length(self) + return self._length +end + +function get.track_id(self) + return self._track_id +end + +function get.author(self) + return self._author +end + return Track diff --git a/libs/utils.lua b/libs/utils.lua index e8cbdb6..a75853f 100644 --- a/libs/utils.lua +++ b/libs/utils.lua @@ -68,6 +68,10 @@ function randval(tb) return tb[math.random(1, #tb)] end +function trim(str) + return string.match(str, '^%s*(.-)%s*$') +end + return { dump = dump, interp = interp, @@ -75,5 +79,6 @@ return { valueintb = valueintb, escape = escape, starts_with = starts_with, - shift = shift + shift = shift, + trim = trim } diff --git a/package.lua b/package.lua index e8bc0a4..8a57b1d 100644 --- a/package.lua +++ b/package.lua @@ -1,15 +1,14 @@ - return { - name = "flare", - version = "0.0.1", - description = "A fully-featured Lavalink client library for Lua that works seamlessly with Discordia.", - tags = { "discord", "api", "lavalink", "discordia", "lua", "lit", "luvit" }, - license = "MIT", - author = { name = "cloudwithax", email = "heyimnick14@gmail.com" }, - homepage = "https://github.com/cloudwithax/flare", - dependencies = {}, - files = { - "**.lua", - "!test*" - } +return { + name = "flare", + version = "1.0.0", + description = "A fully-featured Lavalink client library for Lua that works seamlessly with Discordia.", + tags = { "discord", "api", "lavalink", "discordia", "lua", "lit", "luvit" }, + license = "MIT", + author = { name = "cloudwithax", email = "heyimnick14@gmail.com" }, + homepage = "https://github.com/cloudwithax/flare", + dependencies = {}, + files = { + "**.lua", + "!test*" } - \ No newline at end of file +}