feature: add player
This commit is contained in:
parent
9922b2d8db
commit
9b3baebda1
12
lavalink.go
12
lavalink.go
|
@ -12,11 +12,14 @@ type Lavalink struct {
|
||||||
UserID int
|
UserID int
|
||||||
|
|
||||||
nodes []Node
|
nodes []Node
|
||||||
|
players map[string]*Player
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errNoNodes = errors.New("No nodes present")
|
errNoNodes = errors.New("No nodes present")
|
||||||
errNodeNotFound = errors.New("Couldn't find that node")
|
errNodeNotFound = errors.New("Couldn't find that node")
|
||||||
|
errPlayerNotFound = errors.New("Couldn't find a player for that guild")
|
||||||
|
errVolumeOutOfRange = errors.New("Volume is out of range, must be within [0, 1000]")
|
||||||
errInvalidVersion = errors.New("This library requires Lavalink >= 3")
|
errInvalidVersion = errors.New("This library requires Lavalink >= 3")
|
||||||
errUnknownPayload = errors.New("Lavalink sent an unknown payload")
|
errUnknownPayload = errors.New("Lavalink sent an unknown payload")
|
||||||
)
|
)
|
||||||
|
@ -74,3 +77,12 @@ func (lavalink *Lavalink) BestNode() (*Node, error) {
|
||||||
// TODO: lookup latency
|
// TODO: lookup latency
|
||||||
return &lavalink.nodes[0], nil
|
return &lavalink.nodes[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPlayer gets a player for a guild
|
||||||
|
func (lavalink *Lavalink) GetPlayer(guild string) (*Player, error) {
|
||||||
|
p, ok := lavalink.players[guild]
|
||||||
|
if !ok {
|
||||||
|
return nil, errPlayerNotFound
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
43
model.go
43
model.go
|
@ -51,3 +51,46 @@ type TrackInfo struct {
|
||||||
Length int `json:"length"`
|
Length int `json:"length"`
|
||||||
Position int `json:"position"`
|
Position int `json:"position"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
opVoiceUpdate = "voiceUpdate"
|
||||||
|
opPlay = "play"
|
||||||
|
opStop = "stop"
|
||||||
|
opPause = "pause"
|
||||||
|
opSeek = "seek"
|
||||||
|
opVolume = "volume"
|
||||||
|
opDestroy = "destroy"
|
||||||
|
opPlayerUpdate = "playerUpdate"
|
||||||
|
opEvent = "event"
|
||||||
|
)
|
||||||
|
|
||||||
|
type message struct {
|
||||||
|
Op string `json:"op"`
|
||||||
|
GuildID string `json:"guildId,omitempty"`
|
||||||
|
SessionID string `json:"sessionId,omitempty"`
|
||||||
|
Event *VoiceServerUpdate `json:"event,omitempty"`
|
||||||
|
Track string `json:"track,omitempty"`
|
||||||
|
StartTime string `json:"startTime,omitempty"`
|
||||||
|
EndTime string `json:"endTime,omitempty"`
|
||||||
|
Pause bool `json:"pause,omitempty"`
|
||||||
|
Position int `json:"position,omitempty"`
|
||||||
|
Volume int `json:"volume,omitempty"`
|
||||||
|
State *state `json:"state,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
Reason string `json:"reason,omitempty"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
ThresholdMs int `json:"thresholdMs,omitempty"`
|
||||||
|
// TODO: stats
|
||||||
|
}
|
||||||
|
|
||||||
|
type state struct {
|
||||||
|
Time int `json:"time"`
|
||||||
|
Position int `json:"position"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VoiceServerUpdate is a raw Discord VOICE_SERVER_UPDATE event
|
||||||
|
type VoiceServerUpdate struct {
|
||||||
|
GuildID int `json:"guild_id"`
|
||||||
|
Endpoint string `json:"endpoint"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
41
node.go
41
node.go
|
@ -93,9 +93,50 @@ func (node *Node) onEvent(msgType int, msg []byte) error {
|
||||||
if msgType != websocket.TextMessage {
|
if msgType != websocket.TextMessage {
|
||||||
return errUnknownPayload
|
return errUnknownPayload
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m := message{}
|
||||||
|
err := json.Unmarshal(msg, &m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch m.Op {
|
||||||
|
case opPlayerUpdate:
|
||||||
|
// todo
|
||||||
|
case opEvent:
|
||||||
|
// todo
|
||||||
|
default:
|
||||||
|
return errUnknownPayload
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreatePlayer creates an audio player on this node
|
||||||
|
func (node *Node) CreatePlayer(guildID string, sessionID string, event VoiceServerUpdate) (*Player, error) {
|
||||||
|
msg := message{
|
||||||
|
Op: opVoiceUpdate,
|
||||||
|
GuildID: guildID,
|
||||||
|
SessionID: sessionID,
|
||||||
|
Event: &event,
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = node.wsConn.WriteMessage(websocket.TextMessage, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
player := &Player{
|
||||||
|
guildID: guildID,
|
||||||
|
manager: node.manager,
|
||||||
|
node: node,
|
||||||
|
}
|
||||||
|
node.manager.players[guildID] = player
|
||||||
|
return player, nil
|
||||||
|
}
|
||||||
|
|
||||||
// LoadTracks queries lavalink to return a Tracks object
|
// LoadTracks queries lavalink to return a Tracks object
|
||||||
//
|
//
|
||||||
// query should be a valid Lavaplayer query, including but not limited to:
|
// query should be a valid Lavaplayer query, including but not limited to:
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
package gavalink
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Player is a Lavalink player
|
||||||
|
type Player struct {
|
||||||
|
guildID string
|
||||||
|
manager *Lavalink
|
||||||
|
node *Node
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play will play the given track completely
|
||||||
|
func (player *Player) Play(track string) error {
|
||||||
|
return player.PlayAt(track, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlayAt will play the given track at the specified start and end times
|
||||||
|
//
|
||||||
|
// Setting a time to 0 will omit it.
|
||||||
|
func (player *Player) PlayAt(track string, startTime int, endTime int) error {
|
||||||
|
start := strconv.Itoa(startTime)
|
||||||
|
end := strconv.Itoa(endTime)
|
||||||
|
|
||||||
|
msg := message{
|
||||||
|
Op: opPlay,
|
||||||
|
Track: track,
|
||||||
|
StartTime: start,
|
||||||
|
EndTime: end,
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = player.node.wsConn.WriteMessage(websocket.TextMessage, data)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop will stop the currently playing track
|
||||||
|
func (player *Player) Stop() error {
|
||||||
|
msg := message{
|
||||||
|
Op: opStop,
|
||||||
|
GuildID: player.guildID,
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = player.node.wsConn.WriteMessage(websocket.TextMessage, data)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause will pause or resume the player, depending on the pause parameter
|
||||||
|
func (player *Player) Pause(pause bool) error {
|
||||||
|
msg := message{
|
||||||
|
Op: opPause,
|
||||||
|
GuildID: player.guildID,
|
||||||
|
Pause: pause,
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = player.node.wsConn.WriteMessage(websocket.TextMessage, data)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek will seek the player to the speicifed position, in millis
|
||||||
|
func (player *Player) Seek(position int) error {
|
||||||
|
msg := message{
|
||||||
|
Op: opSeek,
|
||||||
|
GuildID: player.guildID,
|
||||||
|
Position: position,
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = player.node.wsConn.WriteMessage(websocket.TextMessage, data)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Volume will set the player's volume to the specified value
|
||||||
|
//
|
||||||
|
// volume must be within [0, 1000]
|
||||||
|
func (player *Player) Volume(volume int) error {
|
||||||
|
if volume < 0 || volume > 1000 {
|
||||||
|
return errVolumeOutOfRange
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := message{
|
||||||
|
Op: opVolume,
|
||||||
|
GuildID: player.guildID,
|
||||||
|
Volume: volume,
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = player.node.wsConn.WriteMessage(websocket.TextMessage, data)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy will destroy this player
|
||||||
|
func (player *Player) Destroy() error {
|
||||||
|
msg := message{
|
||||||
|
Op: opDestroy,
|
||||||
|
GuildID: player.guildID,
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = player.node.wsConn.WriteMessage(websocket.TextMessage, data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
player.manager.players[player.guildID] = nil
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue