feature: add player

This commit is contained in:
Christopher F 2018-08-22 18:44:20 -04:00
parent 9922b2d8db
commit 9b3baebda1
4 changed files with 225 additions and 5 deletions

View File

@ -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
}

View File

@ -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
View File

@ -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:

124
player.go Normal file
View File

@ -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
}