2019-01-13 23:37:38 +00:00
|
|
|
package gavalink
|
|
|
|
|
2019-02-04 00:31:53 +00:00
|
|
|
import (
|
2020-04-12 16:03:08 +00:00
|
|
|
"encoding/json"
|
2019-02-04 00:31:53 +00:00
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
2020-04-12 16:03:08 +00:00
|
|
|
"time"
|
2019-02-04 00:31:53 +00:00
|
|
|
)
|
|
|
|
|
2019-01-13 23:37:38 +00:00
|
|
|
const (
|
|
|
|
// TrackLoaded is a Tracks Type for a succesful single track load
|
|
|
|
TrackLoaded = "TRACK_LOADED"
|
|
|
|
// PlaylistLoaded is a Tracks Type for a succseful playlist load
|
|
|
|
PlaylistLoaded = "PLAYLIST_LOADED"
|
|
|
|
// SearchResult is a Tracks Type for a search containing many tracks
|
|
|
|
SearchResult = "SEARCH_RESULT"
|
|
|
|
// NoMatches is a Tracks Type for a query yielding no matches
|
|
|
|
NoMatches = "NO_MATCHES"
|
|
|
|
// LoadFailed is a Tracks Type for an internal Lavalink error
|
|
|
|
LoadFailed = "LOAD_FAILED"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Tracks contains data for a Lavalink Tracks response
|
|
|
|
type Tracks struct {
|
|
|
|
// Type contains the type of response
|
|
|
|
//
|
|
|
|
// This will be one of TrackLoaded, PlaylistLoaded, SearchResult,
|
|
|
|
// NoMatches, or LoadFailed
|
|
|
|
Type string `json:"loadType"`
|
|
|
|
PlaylistInfo *PlaylistInfo `json:"playlistInfo"`
|
2020-04-12 16:03:08 +00:00
|
|
|
Tracks []*Track `json:"tracks"`
|
2019-01-13 23:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// PlaylistInfo contains information about a loaded playlist
|
|
|
|
type PlaylistInfo struct {
|
|
|
|
// Name is the friendly of the playlist
|
|
|
|
Name string `json:"name"`
|
|
|
|
// SelectedTrack is the index of the track that loaded the playlist,
|
|
|
|
// if one is present.
|
|
|
|
SelectedTrack int `json:"selectedTrack"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Track contains information about a loaded track
|
|
|
|
type Track struct {
|
|
|
|
// Data contains the base64 encoded Lavaplayer track
|
2020-04-12 16:03:08 +00:00
|
|
|
Data string `json:"track"`
|
|
|
|
Info *TrackInfo `json:"info"`
|
2019-01-13 23:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TrackInfo contains more data about a loaded track
|
|
|
|
type TrackInfo struct {
|
2020-04-12 16:03:08 +00:00
|
|
|
Identifier string `json:"identifier"`
|
|
|
|
Title string `json:"title"`
|
|
|
|
Author string `json:"author"`
|
|
|
|
URI string `json:"uri"`
|
|
|
|
Seekable bool `json:"isSeekable"`
|
|
|
|
Stream bool `json:"isStream"`
|
|
|
|
Length time.Duration `json:"length"`
|
|
|
|
Position int `json:"position"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *TrackInfo) MarshalJSON() ([]byte, error) {
|
|
|
|
type Alias TrackInfo
|
|
|
|
return json.Marshal(&struct {
|
|
|
|
Length int64 `json:"length"`
|
|
|
|
*Alias
|
|
|
|
}{
|
|
|
|
Length: int64(t.Length / time.Millisecond),
|
|
|
|
Alias: (*Alias)(t),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *TrackInfo) UnmarshalJSON(data []byte) error {
|
|
|
|
type Alias TrackInfo
|
|
|
|
aux := &struct {
|
|
|
|
Length int64 `json:"length"`
|
|
|
|
*Alias
|
|
|
|
}{
|
|
|
|
Alias: (*Alias)(t),
|
|
|
|
}
|
|
|
|
if err := json.Unmarshal(data, &aux); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
t.Length = time.Duration(aux.Length) * time.Millisecond
|
|
|
|
return nil
|
2019-01-13 23:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
opVoiceUpdate = "voiceUpdate"
|
2019-02-04 00:31:53 +00:00
|
|
|
opVoiceProcessed = "voiceProcessed"
|
|
|
|
opUserJoin = "userJoin"
|
|
|
|
opUserLeave = "userLeave"
|
|
|
|
opUserListen = "userListen"
|
2019-01-13 23:37:38 +00:00
|
|
|
opPlay = "play"
|
|
|
|
opStop = "stop"
|
|
|
|
opPause = "pause"
|
|
|
|
opSeek = "seek"
|
|
|
|
opVolume = "volume"
|
|
|
|
opDestroy = "destroy"
|
|
|
|
opPlayerUpdate = "playerUpdate"
|
|
|
|
opEvent = "event"
|
|
|
|
opStats = "stats"
|
2019-02-04 00:31:53 +00:00
|
|
|
eventTrackStart = "TrackStartEvent"
|
2019-01-13 23:37:38 +00:00
|
|
|
eventTrackEnd = "TrackEndEvent"
|
|
|
|
eventTrackException = "TrackExceptionEvent"
|
|
|
|
eventTrackStuck = "TrackStuckEvent"
|
|
|
|
)
|
|
|
|
|
|
|
|
// VoiceServerUpdate is a raw Discord VOICE_SERVER_UPDATE event
|
|
|
|
type VoiceServerUpdate struct {
|
|
|
|
GuildID string `json:"guild_id"`
|
|
|
|
Endpoint string `json:"endpoint"`
|
|
|
|
Token string `json:"token"`
|
|
|
|
}
|
2019-02-04 00:31:53 +00:00
|
|
|
|
|
|
|
// VoiceProcessingData is an event containing methods to easily download captured voice data
|
|
|
|
type VoiceProcessingData struct {
|
|
|
|
io.ReadCloser
|
|
|
|
|
2019-10-14 23:18:25 +00:00
|
|
|
node *Node
|
|
|
|
|
2019-10-14 02:58:25 +00:00
|
|
|
UserID string
|
2019-04-13 20:50:35 +00:00
|
|
|
URL string
|
|
|
|
File string
|
2019-02-04 00:31:53 +00:00
|
|
|
|
|
|
|
res *http.Response
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *VoiceProcessingData) open() error {
|
2019-10-14 23:18:25 +00:00
|
|
|
req, err := http.NewRequest(http.MethodGet, v.URL, nil)
|
|
|
|
|
|
|
|
req.Header.Set("Authorization", v.node.config.Password)
|
|
|
|
|
|
|
|
res, err := v.node.client.Do(req)
|
2019-02-04 00:31:53 +00:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
v.res = res
|
2019-04-13 20:50:35 +00:00
|
|
|
|
|
|
|
return nil
|
2019-02-04 00:31:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (v *VoiceProcessingData) Close() error {
|
|
|
|
if v.res != nil {
|
|
|
|
return v.res.Body.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *VoiceProcessingData) Read(buf []byte) (n int, err error) {
|
|
|
|
if v.res == nil {
|
|
|
|
err = v.open()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
n, err = v.res.Body.Read(buf)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *VoiceProcessingData) SaveTo(file string) error {
|
|
|
|
f, err := os.Create(file)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
if v.res == nil {
|
|
|
|
err = v.open()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
defer v.res.Body.Close()
|
|
|
|
|
2019-04-13 20:50:35 +00:00
|
|
|
_, err = io.Copy(f, v.res.Body)
|
2019-02-04 00:31:53 +00:00
|
|
|
|
|
|
|
return err
|
|
|
|
}
|