Compare commits

...

1 Commits

Author SHA1 Message Date
Stein Ivar Berghei 8aa79a1e13 Restructure to not use a App struct 2022-12-22 12:43:40 +01:00
10 changed files with 229 additions and 399 deletions

47
bot.go
View File

@ -9,17 +9,9 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/diamondburned/arikawa/v3/state"
"github.com/diamondburned/arikawa/v3/voice"
"github.com/faiface/beep"
"github.com/fhs/gompd/v2/mpd"
"github.com/gohugoio/hugo/cache/filecache" "github.com/gohugoio/hugo/cache/filecache"
"github.com/jackc/pgx/v5"
"github.com/julienschmidt/httprouter"
"github.com/kataras/go-events"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/viper" "github.com/spf13/viper"
"google.golang.org/api/youtube/v3"
) )
const ( const (
@ -30,8 +22,9 @@ const (
) )
var ( var (
app = new(App)
config = viper.GetViper() config = viper.GetViper()
bfs = afero.NewBasePathFs(afero.NewOsFs(), "cache")
cache = filecache.NewCache(bfs, 1*time.Hour, "")
) )
func init() { func init() {
@ -46,34 +39,12 @@ func init() {
log.Fatal(err) log.Fatal(err)
} }
app.plmutex = &sync.Mutex{} mpd_mutex = &sync.Mutex{}
log.Println("bot.go done.") log.Println("bot.go done.")
} }
type App struct {
discord *state.State
voice *voice.Session
youtube *youtube.Service
ambiance beep.Mixer
curamb Ambiance
events events.EventEmmiter
db *pgx.Conn
router *httprouter.Router
active []string
plidx int
cache *filecache.Cache
mpdc context.CancelFunc
mpdw *mpd.Watcher
mpd *mpd.Client
plmutex *sync.Mutex
plcancel context.CancelFunc
}
func main() { func main() {
bfs := afero.NewBasePathFs(afero.NewOsFs(), "cache")
app.cache = filecache.NewCache(bfs, 1*time.Hour, "")
ticker := time.NewTicker(300 * time.Millisecond) ticker := time.NewTicker(300 * time.Millisecond)
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, os.Interrupt) ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
@ -82,15 +53,15 @@ func main() {
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
app.db.Close(ctx) db.Close(ctx)
app.mpdw.Close() mpdw.Close()
app.mpdc() mpdcf()
app.voice.Leave(ctx) dvoice.Leave(ctx)
dgvc() dgvc()
app.discord.Close() dstate.Close()
return return
case <-ticker.C: case <-ticker.C:
app.events.Emit("tick") ev.Emit("tick")
} }
} }
} }

16
db.go
View File

@ -8,11 +8,13 @@ import (
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
) )
var db *pgx.Conn
func init() { func init() {
log.Println("db.go loading..") log.Println("db.go loading..")
var err error var err error
app.db, err = pgx.Connect(context.Background(), config.GetString("db.connstring")) db, err = pgx.Connect(context.Background(), config.GetString("db.connstring"))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -25,8 +27,8 @@ type Playlist struct {
Title string Title string
} }
func (app App) GetPlaylists() (playlists []Playlist, err error) { func GetPlaylists() (playlists []Playlist, err error) {
rows, err := app.db.Query(context.Background(), "SELECT id, url, title FROM playlists") rows, err := db.Query(context.Background(), "SELECT id, url, title FROM playlists")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -35,8 +37,8 @@ func (app App) GetPlaylists() (playlists []Playlist, err error) {
return return
} }
func (app App) GetPlaylist(id uuid.UUID) (*Playlist, error) { func GetPlaylist(id uuid.UUID) (*Playlist, error) {
rows, err := app.db.Query(context.Background(), "SELECT id, url, title FROM playlists where id=$1 limit 1", id) rows, err := db.Query(context.Background(), "SELECT id, url, title FROM playlists where id=$1 limit 1", id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -50,9 +52,9 @@ func (app App) GetPlaylist(id uuid.UUID) (*Playlist, error) {
return out, nil return out, nil
} }
func (app App) AddPlaylist(title string, uri string) (uuid.UUID, error) { func AddPlaylist(title string, uri string) (uuid.UUID, error) {
id := uuid.New() id := uuid.New()
_, err := app.db.Exec(context.Background(), "INSERT INTO playlists VALUES ($1, $2, $3)", id, title, uri) _, err := db.Exec(context.Background(), "INSERT INTO playlists VALUES ($1, $2, $3)", id, title, uri)
if err != nil { if err != nil {
return *new(uuid.UUID), err return *new(uuid.UUID), err
} }

View File

@ -11,7 +11,11 @@ import (
"github.com/diamondburned/arikawa/v3/voice/voicegateway" "github.com/diamondburned/arikawa/v3/voice/voicegateway"
) )
var dgvc context.CancelFunc var (
dstate *state.State
dvoice *voice.Session
dgvc context.CancelFunc
)
func init() { func init() {
log.Println("discord.go loading..") log.Println("discord.go loading..")
@ -47,8 +51,8 @@ func init() {
v.Speaking(ctx, voicegateway.NotSpeaking) v.Speaking(ctx, voicegateway.NotSpeaking)
app.discord = s dstate = s
app.voice = v dvoice = v
discordspeaker.Init(v) discordspeaker.Init(v)

162
events.go
View File

@ -30,40 +30,46 @@ type SongInfo struct {
Song string `json:"song,omitempty"` Song string `json:"song,omitempty"`
} }
var l = rate.Sometimes{Interval: 800 * time.Millisecond} var (
l = rate.Sometimes{Interval: 800 * time.Millisecond}
)
var (
ev events.EventEmmiter
)
func init() { func init() {
log.Println("events.go loading...") log.Println("events.go loading...")
app.events = events.New() ev = events.New()
app.events.On("load_playlist", app.loadPlaylist) ev.On("load_playlist", loadPlaylist)
app.events.On("add_playlist", app.addPlaylist) ev.On("add_playlist", addPlaylist)
//app.events.On("preload_song", app.preloadSong) //ev.On("preload_song", app.preloadSong)
//app.events.On("song_over", app.songInfo) //ev.On("song_over", app.songInfo)
//app.events.On("song_start", app.songInfo) //ev.On("song_start", app.songInfo)
app.events.On("player", app.songInfo) ev.On("player", songInfo)
//app.events.On("song_position", app.songPosition) //ev.On("song_position", app.songPosition)
app.events.On("ambiance_play", app.ambiancePlay) ev.On("ambiance_play", ambiancePlay)
app.events.On("ambiance_stop", app.ambianceStop) ev.On("ambiance_stop", ambianceStop)
app.events.On("ambiance_add", app.ambianceAdd) ev.On("ambiance_add", ambianceAdd)
app.events.On("stop", app.stop) ev.On("stop", stop)
app.events.On("next", app.nextSong) ev.On("next", nextSong)
app.events.On("prev", app.prevSong) ev.On("prev", prevSong)
app.events.On("vol_up", app.volup) ev.On("vol_up", volup)
app.events.On("vol_down", app.voldown) ev.On("vol_down", voldown)
app.events.On("vol_set", app.volset) ev.On("vol_set", volset)
//app.events.On("tick", app.checkQueue) //ev.On("tick", app.checkQueue)
app.events.On("tick", app.songPosition) ev.On("tick", songPosition)
//app.events.On("tick", app.checkTimeleft) //ev.On("tick", app.checkTimeleft)
} }
func (app *App) volup(payload ...interface{}) { func volup(payload ...interface{}) {
if !(len(payload) > 0) { if !(len(payload) > 0) {
log.Println("volup called without a payload.") log.Println("volup called without a payload.")
return return
@ -91,7 +97,7 @@ func (app *App) volup(payload ...interface{}) {
} }
} }
func (app *App) voldown(payload ...interface{}) { func voldown(payload ...interface{}) {
if !(len(payload) > 0) { if !(len(payload) > 0) {
log.Println("voldown called without a payload.") log.Println("voldown called without a payload.")
return return
@ -119,7 +125,7 @@ func (app *App) voldown(payload ...interface{}) {
} }
} }
func (app *App) volset(payload ...interface{}) { func volset(payload ...interface{}) {
if !(len(payload) > 0) { if !(len(payload) > 0) {
log.Println("volset called without a payload.") log.Println("volset called without a payload.")
return return
@ -147,10 +153,10 @@ func (app *App) volset(payload ...interface{}) {
amb_volume.Volume = vol amb_volume.Volume = vol
} }
app.sendVolume() sendVolume()
} }
func (app *App) sendVolume() { func sendVolume() {
msg := make(map[string]interface{}) msg := make(map[string]interface{})
out := make(map[string]float64) out := make(map[string]float64)
msg["event"] = "volume" msg["event"] = "volume"
@ -160,16 +166,16 @@ func (app *App) sendVolume() {
ws_msg <- msg ws_msg <- msg
} }
func (app *App) songInfoEvent(event string) map[string]interface{} { func songInfoEvent(event string) map[string]interface{} {
msg := make(map[string]interface{}) msg := make(map[string]interface{})
msg["event"] = event msg["event"] = event
status, err := app.mpd.Status() status, err := mpdc.Status()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return nil return nil
} }
cur, err := app.mpd.CurrentSong() cur, err := mpdc.CurrentSong()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return nil return nil
@ -211,7 +217,7 @@ func (app *App) songInfoEvent(event string) map[string]interface{} {
return nil return nil
} }
pl, err := app.GetPlaylist(plid) pl, err := GetPlaylist(plid)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return nil return nil
@ -241,7 +247,7 @@ func (app *App) songInfoEvent(event string) map[string]interface{} {
return msg return msg
} }
func (app *App) ambiancePlay(payload ...interface{}) { func ambiancePlay(payload ...interface{}) {
if !(len(payload) > 0) { if !(len(payload) > 0) {
log.Println("ambiance_play called without a payload.") log.Println("ambiance_play called without a payload.")
return return
@ -289,14 +295,14 @@ func (app *App) ambiancePlay(payload ...interface{}) {
} }
discordspeaker.Lock() discordspeaker.Lock()
app.ambiance.Clear() amb_mixer.Clear()
app.ambiance.Add(volume) amb_mixer.Add(volume)
discordspeaker.Unlock() discordspeaker.Unlock()
msg := make(map[string]interface{}) msg := make(map[string]interface{})
out := make(map[string]interface{}) out := make(map[string]interface{})
app.curamb = amb amb_curr = amb
msg["event"] = "ambiance_play" msg["event"] = "ambiance_play"
out["id"] = id out["id"] = id
@ -304,10 +310,10 @@ func (app *App) ambiancePlay(payload ...interface{}) {
ws_msg <- msg ws_msg <- msg
} }
func (app *App) ambianceStop(payload ...interface{}) { func ambianceStop(payload ...interface{}) {
log.Println("Stopping ambiance") log.Println("Stopping ambiance")
discordspeaker.Lock() discordspeaker.Lock()
app.ambiance.Clear() amb_mixer.Clear()
discordspeaker.Unlock() discordspeaker.Unlock()
msg := make(map[string]interface{}) msg := make(map[string]interface{})
@ -316,7 +322,7 @@ func (app *App) ambianceStop(payload ...interface{}) {
} }
func (app *App) ambianceAdd(payload ...interface{}) { func ambianceAdd(payload ...interface{}) {
if !(len(payload) > 0) { if !(len(payload) > 0) {
log.Println("addPlaylist called without a payload.") log.Println("addPlaylist called without a payload.")
return return
@ -359,8 +365,8 @@ func (app *App) ambianceAdd(payload ...interface{}) {
ws_msg <- msg ws_msg <- msg
} }
func (app *App) songPosition(payload ...interface{}) { func songPosition(payload ...interface{}) {
status, err := app.mpd.Status() status, err := mpdc.Status()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return return
@ -395,24 +401,24 @@ func (app *App) songPosition(payload ...interface{}) {
} }
func (app *App) songInfo(payload ...interface{}) { func songInfo(payload ...interface{}) {
log.Println("song_info event received") log.Println("song_info event received")
msg := app.songInfoEvent("song_info") msg := songInfoEvent("song_info")
if msg != nil { if msg != nil {
ws_msg <- msg ws_msg <- msg
} }
} }
func (app *App) stop(payload ...interface{}) { func stop(payload ...interface{}) {
log.Println("stop event received") log.Println("stop event received")
app.plmutex.Lock() mpd_mutex.Lock()
if app.plcancel != nil { if mpd_plcf != nil {
app.plcancel() mpd_plcf()
} }
app.mpd.Stop() mpdc.Stop()
app.plmutex.Unlock() mpd_mutex.Unlock()
msg := make(map[string]interface{}) msg := make(map[string]interface{})
msg["event"] = "stop" msg["event"] = "stop"
@ -420,28 +426,28 @@ func (app *App) stop(payload ...interface{}) {
ws_msg <- msg ws_msg <- msg
} }
func (app *App) prevSong(payload ...interface{}) { func prevSong(payload ...interface{}) {
log.Println("prev_song event received") log.Println("prev_song event received")
app.plmutex.Lock() mpd_mutex.Lock()
err := app.mpd.Previous() err := mpdc.Previous()
app.plmutex.Unlock() mpd_mutex.Unlock()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
} }
} }
func (app *App) nextSong(payload ...interface{}) { func nextSong(payload ...interface{}) {
log.Println("next_song event received") log.Println("next_song event received")
app.plmutex.Lock() mpd_mutex.Lock()
err := app.mpd.Next() err := mpdc.Next()
app.plmutex.Unlock() mpd_mutex.Unlock()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
} }
} }
func (app *App) addPlaylist(payload ...interface{}) { func addPlaylist(payload ...interface{}) {
if !(len(payload) > 0) { if !(len(payload) > 0) {
log.Println("addPlaylist called without a payload.") log.Println("addPlaylist called without a payload.")
return return
@ -481,12 +487,12 @@ func (app *App) addPlaylist(payload ...interface{}) {
return return
} }
_, err = app.Playlist(plid) _, err = YTPlaylist(plid)
if err != nil { if err != nil {
log.Println("Error getting youtube playlist info,", plid) log.Println("Error getting youtube playlist info,", plid)
} }
id, err := app.AddPlaylist(pltitle, plid) id, err := AddPlaylist(pltitle, plid)
if err != nil { if err != nil {
log.Println("Error getting youtube playlist info,", plid) log.Println("Error getting youtube playlist info,", plid)
} }
@ -499,7 +505,7 @@ func (app *App) addPlaylist(payload ...interface{}) {
ws_msg <- msg ws_msg <- msg
} }
func (app *App) loadPlaylist(payload ...interface{}) { func loadPlaylist(payload ...interface{}) {
log.Println("load_playlist event received") log.Println("load_playlist event received")
if !(len(payload) > 0) { if !(len(payload) > 0) {
@ -526,13 +532,13 @@ func (app *App) loadPlaylist(payload ...interface{}) {
} }
log.Println("Loading new playlist: ", id) log.Println("Loading new playlist: ", id)
pl, err := app.GetPlaylist(id) pl, err := GetPlaylist(id)
if err != nil { if err != nil {
log.Println("Unable to find playlist with id,", id) log.Println("Unable to find playlist with id,", id)
return return
} }
list, err := app.Playlist(pl.Url) list, err := YTPlaylist(pl.Url)
if err != nil { if err != nil {
log.Println("Error getting playlist info,", id) log.Println("Error getting playlist info,", id)
return return
@ -543,16 +549,16 @@ func (app *App) loadPlaylist(payload ...interface{}) {
log.Println("Unable to shuffle playlist") log.Println("Unable to shuffle playlist")
return return
} }
app.plmutex.Lock() mpd_mutex.Lock()
if app.plcancel != nil { if mpd_plcf != nil {
app.plcancel() mpd_plcf()
} }
app.mpd.Stop() mpdc.Stop()
app.mpd.Clear() mpdc.Clear()
app.plmutex.Unlock() mpd_mutex.Unlock()
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
app.plcancel = cancel mpd_plcf = cancel
go func() { go func() {
defer cancel() defer cancel()
@ -560,13 +566,13 @@ func (app *App) loadPlaylist(payload ...interface{}) {
for _, vid := range list { for _, vid := range list {
log.Printf("Adding %s", vid) log.Printf("Adding %s", vid)
ytinfo, err := app.Video(vid) ytinfo, err := YTVideo(vid)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
continue continue
} }
_, yt, err := app.cache.GetOrCreateBytes(vid+".txt", func() ([]byte, error) { _, yt, err := cache.GetOrCreateBytes(vid+".txt", func() ([]byte, error) {
uri, err := NewYTdl(vid) uri, err := NewYTdl(vid)
if err != nil { if err != nil {
return nil, err return nil, err
@ -580,8 +586,8 @@ func (app *App) loadPlaylist(payload ...interface{}) {
// Run as a local function so we can defer the mutex unlock incase one of these errors. // Run as a local function so we can defer the mutex unlock incase one of these errors.
ok := func() (ok bool) { ok := func() (ok bool) {
app.plmutex.Lock() mpd_mutex.Lock()
defer app.plmutex.Unlock() defer mpd_mutex.Unlock()
if ctx.Err() != nil { if ctx.Err() != nil {
return false return false
@ -590,41 +596,41 @@ func (app *App) loadPlaylist(payload ...interface{}) {
ok = true ok = true
// state:stop // state:stop
songid, err := app.mpd.AddID(string(yt), 0) songid, err := mpdc.AddID(string(yt), 0)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return return
} }
mpdcmd := app.mpd.Command("%s %d %s %s", mpd.Quoted("addtagid"), songid, mpd.Quoted("artist"), ytinfo.Channel) mpdcmd := mpdc.Command("%s %d %s %s", mpd.Quoted("addtagid"), songid, mpd.Quoted("artist"), ytinfo.Channel)
err = mpdcmd.OK() err = mpdcmd.OK()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return return
} }
mpdcmd = app.mpd.Command("%s %d %s %s", mpd.Quoted("addtagid"), songid, mpd.Quoted("title"), ytinfo.Title) mpdcmd = mpdc.Command("%s %d %s %s", mpd.Quoted("addtagid"), songid, mpd.Quoted("title"), ytinfo.Title)
err = mpdcmd.OK() err = mpdcmd.OK()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return return
} }
mpdcmd = app.mpd.Command("%s %d %s %s", mpd.Quoted("addtagid"), songid, mpd.Quoted("location"), vid) mpdcmd = mpdc.Command("%s %d %s %s", mpd.Quoted("addtagid"), songid, mpd.Quoted("location"), vid)
err = mpdcmd.OK() err = mpdcmd.OK()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return return
} }
mpdcmd = app.mpd.Command("%s %d %s %s", mpd.Quoted("addtagid"), songid, mpd.Quoted("album"), pl.Id.String()) mpdcmd = mpdc.Command("%s %d %s %s", mpd.Quoted("addtagid"), songid, mpd.Quoted("album"), pl.Id.String())
err = mpdcmd.OK() err = mpdcmd.OK()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return return
} }
app.mpd.Play(-1) mpdc.Play(-1)
return return
}() }()

71
mixer.go Normal file
View File

@ -0,0 +1,71 @@
package main
import (
"log"
"time"
"github.com/faiface/beep"
"github.com/faiface/beep/effects"
"dndmusicbot/ffmpeg"
discordspeaker "dndmusicbot/speaker"
)
var (
pl_volume *effects.Volume
amb_volume *effects.Volume
amb_mixer beep.Mixer
amb_curr Ambiance
)
func init() {
log.Println("queue.go loading..")
amb_mixer = beep.Mixer{}
amb_volume = &effects.Volume{
Streamer: &amb_mixer,
Base: 2,
Volume: 2,
Silent: false,
}
discordspeaker.Play(amb_volume)
mpdstream, err := NewMPD()
if err != nil {
log.Fatal(err)
}
pl_volume = &effects.Volume{
Streamer: mpdstream,
Base: 2,
Volume: -2,
Silent: false,
}
discordspeaker.Play(pl_volume)
/*
app.queue = new(Queue)
app.queue.list = list.New()
app.queue.Events = app.events
discordspeaker.Play(app.queue)
*/
log.Println("queue.go done.")
}
type Song struct {
Title string
Channel string
VideoID string
Length time.Duration
PCM *ffmpeg.PCM
Playlist Playlist
DLuri string
}
func (s *Song) NewStream() (err error) {
s.PCM, err = ffmpeg.NewPCM(s.DLuri, sampleRate, channels)
return
}

27
mpd.go
View File

@ -7,6 +7,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"strconv" "strconv"
"sync"
"syscall" "syscall"
"text/template" "text/template"
"time" "time"
@ -21,6 +22,14 @@ type MPD struct {
f beep.Format f beep.Format
} }
var (
mpdcf context.CancelFunc
mpdw *mpd.Watcher
mpdc *mpd.Client
mpd_mutex *sync.Mutex
mpd_plcf context.CancelFunc
)
func init() { func init() {
log.Println("mpd.go loading..") log.Println("mpd.go loading..")
@ -62,7 +71,7 @@ func init() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
app.mpdc = cancel mpdcf = cancel
cmd := exec.CommandContext( cmd := exec.CommandContext(
ctx, ctx,
@ -86,20 +95,20 @@ func init() {
// wait for mpd to start. // wait for mpd to start.
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
app.mpd, err = mpd.Dial("unix", config.GetString("mpd.sock")) mpdc, err = mpd.Dial("unix", config.GetString("mpd.sock"))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
app.mpd.Repeat(true) mpdc.Repeat(true)
app.mpd.Random(true) mpdc.Random(true)
err = app.mpd.EnableOutput(0) err = mpdc.EnableOutput(0)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
app.mpdw, err = mpd.NewWatcher("unix", config.GetString("mpd.sock"), "") mpdw, err = mpd.NewWatcher("unix", config.GetString("mpd.sock"), "")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -107,9 +116,9 @@ func init() {
go func() { go func() {
for { for {
select { select {
case ev := <-app.mpdw.Event: case e := <-mpdw.Event:
app.events.Emit(events.EventName(ev)) ev.Emit(events.EventName(e))
case err := <-app.mpdw.Error: case err := <-mpdw.Error:
log.Println(err) log.Println(err)
return return
} }

203
queue.go
View File

@ -1,203 +0,0 @@
package main
import (
"container/list"
"log"
"time"
"github.com/faiface/beep"
"github.com/faiface/beep/effects"
"github.com/kataras/go-events"
"dndmusicbot/ffmpeg"
discordspeaker "dndmusicbot/speaker"
)
var pl_volume *effects.Volume
var amb_volume *effects.Volume
func init() {
log.Println("queue.go loading..")
app.ambiance = beep.Mixer{}
amb_volume = &effects.Volume{
Streamer: &app.ambiance,
Base: 2,
Volume: 2,
Silent: false,
}
discordspeaker.Play(amb_volume)
mpdstream, err := NewMPD()
if err != nil {
log.Fatal(err)
}
pl_volume = &effects.Volume{
Streamer: mpdstream,
Base: 2,
Volume: -2,
Silent: false,
}
discordspeaker.Play(pl_volume)
/*
app.queue = new(Queue)
app.queue.list = list.New()
app.queue.Events = app.events
discordspeaker.Play(app.queue)
*/
log.Println("queue.go done.")
}
type Song struct {
Title string
Channel string
VideoID string
Length time.Duration
PCM *ffmpeg.PCM
Playlist Playlist
DLuri string
}
func (s *Song) NewStream() (err error) {
s.PCM, err = ffmpeg.NewPCM(s.DLuri, sampleRate, channels)
return
}
type Queue struct {
Events events.EventEmmiter
playing bool
list *list.List
current *list.Element
}
func (q Queue) IsPlaying() bool {
return q.playing
}
func (q *Queue) Play() {
q.playing = true
if q.list.Len() > 0 {
play := q.list.Front()
q.current = play
app.events.Emit("song_start")
}
}
func (q *Queue) Add(s *Song) {
el := q.list.PushBack(s)
if el == q.list.Front() {
q.Play()
}
}
func (q Queue) QLen() int {
return q.list.Len()
}
func (q Queue) Current() *Song {
if q.current == nil {
return new(Song)
}
return q.current.Value.(*Song)
}
func (q *Queue) Reset() {
err := app.mpd.Clear()
if err != nil {
log.Println(err)
}
}
func (q *Queue) Next() {
if q.current == nil {
return
}
next := q.current.Next()
if next == nil {
next = q.list.Front()
}
pcm := next.Value.(*Song).PCM
if pcm != nil && pcm.Position() != 0 {
err := next.Value.(*Song).NewStream()
log.Println(err)
}
q.current = next
}
func (q *Queue) Prev() {
if q.current == nil {
return
}
prev := q.current.Prev()
if prev == nil {
prev = q.list.Back()
}
err := prev.Value.(*Song).NewStream()
if err != nil {
log.Println(err)
}
q.current = prev
}
func (q *Queue) Preload() {
}
func (q *Queue) Stream(samples [][2]float64) (n int, ok bool) {
// We use the filled variable to track how many samples we've
// successfully filled already. We loop until all samples are filled.
filled := 0
for filled < len(samples) {
// There are no streamers in the queue, so we stream silence.
if q.current == nil || q.list.Len() == 0 {
for i := range samples[filled:] {
samples[i][0] = 0
samples[i][1] = 0
}
break
}
// We stream from the first streamer in the queue.
n, ok := q.current.Value.(*Song).PCM.Stream(samples[filled:])
// If it's drained, we pop it from the queue, thus continuing with
// the next streamer.
if !ok {
q.Next()
q.Events.Emit("song_over", nil)
}
// We update the number of filled samples.
filled += n
}
return len(samples), true
}
func (q *Queue) Err() error {
return nil
}
func (q *Queue) Position() int {
if q.current == nil || q.current.Value.(*Song).PCM == nil {
return 0
}
return q.current.Value.(*Song).PCM.Position()
}
func (q Queue) Len() int {
if q.current == nil {
return 0
}
return int(q.current.Value.(*Song).Length.Milliseconds())
}

View File

@ -18,16 +18,18 @@ var upgrader = websocket.Upgrader{
WriteBufferSize: 1024, WriteBufferSize: 1024,
} }
var router *httprouter.Router
func init() { func init() {
app.router = httprouter.New() router = httprouter.New()
app.router.GET("/", app.Index) router.GET("/", Index)
app.router.GET("/play/:playlist", app.Play) router.GET("/play/:playlist", Play)
app.router.GET("/reset", app.Reset) router.GET("/reset", Reset)
app.router.GET("/public/*js", app.ServeFiles) router.GET("/public/*js", ServeFiles)
app.router.GET("/css/*css", app.ServeFiles) router.GET("/css/*css", ServeFiles)
app.router.HandlerFunc("GET", "/ws", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { router.HandlerFunc("GET", "/ws", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("WS connection from %v\n", r.RemoteAddr) log.Printf("WS connection from %v\n", r.RemoteAddr)
conn, err := upgrader.Upgrade(w, r, nil) conn, err := upgrader.Upgrade(w, r, nil)
if err != nil { if err != nil {
@ -42,7 +44,7 @@ func init() {
})) }))
go func() { go func() {
log.Fatal(http.ListenAndServe(":8824", app.router)) log.Fatal(http.ListenAndServe(":8824", router))
}() }()
} }
@ -51,7 +53,7 @@ type IndexData struct {
Ambiance []Ambiance Ambiance []Ambiance
} }
func (app App) ServeFiles(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { func ServeFiles(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
filePath := filepath.Join(".", r.URL.Path) filePath := filepath.Join(".", r.URL.Path)
file, err := os.Open(filePath) file, err := os.Open(filePath)
@ -73,8 +75,8 @@ func (app App) ServeFiles(w http.ResponseWriter, r *http.Request, _ httprouter.P
http.ServeContent(w, r, filename, t, file) http.ServeContent(w, r, filename, t, file)
} }
func (app App) Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
playlists, err := app.GetPlaylists() playlists, err := GetPlaylists()
if err != nil { if err != nil {
http.Error(w, "Unable to get playlists. "+err.Error(), http.StatusInternalServerError) http.Error(w, "Unable to get playlists. "+err.Error(), http.StatusInternalServerError)
} }
@ -94,11 +96,11 @@ func (app App) Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params
} }
} }
func (app *App) Play(w http.ResponseWriter, r *http.Request, p httprouter.Params) { func Play(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
plname := p.ByName("playlist") plname := p.ByName("playlist")
if plname == "reset" { if plname == "reset" {
app.events.Emit("stop", nil) ev.Emit("stop", nil)
return return
} }
@ -107,14 +109,14 @@ func (app *App) Play(w http.ResponseWriter, r *http.Request, p httprouter.Params
http.Error(w, "Unable to parse uuid. "+err.Error(), http.StatusInternalServerError) http.Error(w, "Unable to parse uuid. "+err.Error(), http.StatusInternalServerError)
} }
app.events.Emit("new_playlist", plid) ev.Emit("new_playlist", plid)
} }
func (app *App) Add(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { func Add(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
r.ParseForm() r.ParseForm()
} }
func (app *App) Reset(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { func Reset(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
app.events.Emit("stop", nil) ev.Emit("stop", nil)
} }

8
ws.go
View File

@ -64,7 +64,7 @@ func handleWS(c *websocket.Conn) error {
return nil return nil
}) })
msg := app.songInfoEvent("song_info") msg := songInfoEvent("song_info")
vol := make(map[string]interface{}) vol := make(map[string]interface{})
volout := make(map[string]float64) volout := make(map[string]float64)
@ -77,11 +77,11 @@ func handleWS(c *websocket.Conn) error {
c.WriteJSON(msg) c.WriteJSON(msg)
c.WriteJSON(vol) c.WriteJSON(vol)
if app.ambiance.Len() > 0 { if amb_mixer.Len() > 0 {
msg := make(map[string]interface{}) msg := make(map[string]interface{})
out := make(map[string]interface{}) out := make(map[string]interface{})
msg["event"] = "ambiance_play" msg["event"] = "ambiance_play"
out["id"] = app.curamb.Id out["id"] = amb_curr.Id
msg["payload"] = out msg["payload"] = out
c.WriteJSON(msg) c.WriteJSON(msg)
} else { } else {
@ -97,6 +97,6 @@ func handleWS(c *websocket.Conn) error {
return err return err
} }
app.events.Emit(events.EventName(msg.Event), msg.Payload) ev.Emit(events.EventName(msg.Event), msg.Payload)
} }
} }

View File

@ -9,7 +9,6 @@ import (
"errors" "errors"
"io" "io"
"log" "log"
"math/big"
"time" "time"
mrand "math/rand" mrand "math/rand"
@ -19,6 +18,8 @@ import (
"google.golang.org/api/youtube/v3" "google.golang.org/api/youtube/v3"
) )
var yt_svc *youtube.Service
func init() { func init() {
log.Println("youtube.go loading..") log.Println("youtube.go loading..")
@ -26,7 +27,7 @@ func init() {
apikey := config.GetString("youtube.apikey") apikey := config.GetString("youtube.apikey")
app.youtube, err = youtube.NewService(context.Background(), option.WithAPIKey(apikey)) yt_svc, err = youtube.NewService(context.Background(), option.WithAPIKey(apikey))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -46,39 +47,6 @@ func ShufflePlaylist(list []string) ([]string, error) {
return list, nil return list, nil
} }
func (app *App) GetSong(list []string) string {
return list[app.plidx]
}
func (app *App) GetNextSong(list []string) string {
app.plidx++
if app.plidx >= len(app.active) {
app.plidx = 0
}
return list[app.plidx]
}
func (app *App) GetPrevSong(list []string) string {
app.plidx--
if app.plidx < 0 {
app.plidx = len(list)
}
return list[app.plidx]
}
func GetRandomSong(list []string) string {
if !(len(list) > 0) {
return ""
}
idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(list)-1)))
if err != nil {
log.Println("Failed to get random int, ", err)
return ""
}
return list[idx.Int64()]
}
type VideoInfo struct { type VideoInfo struct {
Title string Title string
Channel string Channel string
@ -86,9 +54,9 @@ type VideoInfo struct {
Uri string Uri string
} }
func (app App) Video(vid string) (out VideoInfo, err error) { func YTVideo(vid string) (out VideoInfo, err error) {
_, r, err := app.cache.GetOrCreate(vid+".videoinfo", func() (io.ReadCloser, error) { _, r, err := cache.GetOrCreate(vid+".videoinfo", func() (io.ReadCloser, error) {
call := app.youtube.Videos.List([]string{"snippet", "contentDetails"}) call := yt_svc.Videos.List([]string{"snippet", "contentDetails"})
call.MaxResults(1) call.MaxResults(1)
call.Id(vid) call.Id(vid)
resp, err := call.Do() resp, err := call.Do()
@ -128,8 +96,8 @@ func (app App) Video(vid string) (out VideoInfo, err error) {
return out, nil return out, nil
} }
func (app App) Playlist(playlist string) ([]string, error) { func YTPlaylist(playlist string) ([]string, error) {
call := app.youtube.PlaylistItems.List([]string{"contentDetails"}) call := yt_svc.PlaylistItems.List([]string{"contentDetails"})
pageToken := "" pageToken := ""
call = call.MaxResults(50) call = call.MaxResults(50)
call = call.PlaylistId(playlist) call = call.PlaylistId(playlist)