Compare commits
3 Commits
ec7900ff63
...
48677ffe44
Author | SHA1 | Date |
---|---|---|
Trond Ekseth | 48677ffe44 | |
Trond Ekseth | 1740ee4ffd | |
Trond Ekseth | 18ed3982f6 |
|
@ -7,7 +7,6 @@ cookies.txt
|
|||
config
|
||||
css/*.min.css
|
||||
js/*.min.js
|
||||
cache
|
||||
|
||||
bin
|
||||
ambiance/*.mp3
|
||||
|
|
9
bot.go
9
bot.go
|
@ -11,13 +11,10 @@ import (
|
|||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/dpup/gohubbub"
|
||||
"github.com/faiface/beep"
|
||||
"github.com/gohugoio/hugo/cache/filecache"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/kataras/go-events"
|
||||
"github.com/r3labs/sse/v2"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/viper"
|
||||
"google.golang.org/api/youtube/v3"
|
||||
)
|
||||
|
@ -51,7 +48,7 @@ type App struct {
|
|||
voice *discordgo.VoiceConnection
|
||||
youtube *youtube.Service
|
||||
queue *Queue
|
||||
ambiance beep.Mixer
|
||||
ambiance *Queue
|
||||
curamb string
|
||||
events events.EventEmmiter
|
||||
next bool
|
||||
|
@ -63,13 +60,9 @@ type App struct {
|
|||
playlist *Playlist
|
||||
plm *sync.RWMutex
|
||||
hubbub *gohubbub.Client
|
||||
cache *filecache.Cache
|
||||
}
|
||||
|
||||
func main() {
|
||||
bfs := afero.NewBasePathFs(afero.NewOsFs(), "cache")
|
||||
app.cache = filecache.NewCache(bfs, 1*time.Hour, "")
|
||||
|
||||
app.plm = &sync.RWMutex{}
|
||||
ticker := time.NewTicker(300 * time.Millisecond)
|
||||
|
||||
|
|
172
events.go
172
events.go
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"dndmusicbot/ffmpeg"
|
||||
"dndmusicbot/loop"
|
||||
discordspeaker "dndmusicbot/speaker"
|
||||
"dndmusicbot/ytdl"
|
||||
"encoding/json"
|
||||
|
@ -12,7 +13,6 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/faiface/beep"
|
||||
"github.com/faiface/beep/mp3"
|
||||
"github.com/google/uuid"
|
||||
"github.com/kataras/go-events"
|
||||
|
@ -20,14 +20,14 @@ import (
|
|||
)
|
||||
|
||||
type SongInfo struct {
|
||||
Playlist uuid.UUID `json:"playlist,omitempty"`
|
||||
PlaylistName string `json:"playlistname,omitempty"`
|
||||
Title string `json:"current,omitempty"`
|
||||
Channel string `json:"channel,omitempty"`
|
||||
Playlist uuid.UUID `json:"playlist"`
|
||||
PlaylistName string `json:"playlistname"`
|
||||
Title string `json:"current"`
|
||||
Channel string `json:"channel"`
|
||||
Position int `json:"position"`
|
||||
Length int `json:"len,omitempty"`
|
||||
Length int `json:"len"`
|
||||
Pause bool `json:"pause"`
|
||||
Song string `json:"song,omitempty"`
|
||||
Song string `json:"song"`
|
||||
}
|
||||
|
||||
var l = rate.Sometimes{Interval: 1 * time.Second}
|
||||
|
@ -39,9 +39,8 @@ func init() {
|
|||
app.events.On("load_playlist", app.loadPlaylist)
|
||||
app.events.On("add_playlist", app.addPlaylist)
|
||||
|
||||
//app.events.On("preload_song", app.preloadSong)
|
||||
app.events.On("song_over", app.songInfo)
|
||||
app.events.On("song_start", app.songInfo)
|
||||
app.events.On("preload_song", app.preloadSong)
|
||||
app.events.On("song_over", app.songOver)
|
||||
//app.events.On("song_position", app.songPosition)
|
||||
|
||||
app.events.On("ambiance_play", app.ambiancePlay)
|
||||
|
@ -61,15 +60,37 @@ func (app *App) songInfoEvent(event string) map[string]interface{} {
|
|||
msg := make(map[string]interface{})
|
||||
msg["event"] = event
|
||||
|
||||
var title, channel string
|
||||
|
||||
switch current := app.queue.Current().(type) {
|
||||
case *ffmpeg.PCM:
|
||||
title = current.Player.Title
|
||||
channel = current.Player.Channel
|
||||
}
|
||||
|
||||
var plid uuid.UUID
|
||||
var pltitle string
|
||||
|
||||
if app.playlist != nil {
|
||||
plid = app.playlist.Id
|
||||
pltitle = app.playlist.Title
|
||||
}
|
||||
|
||||
var song string
|
||||
|
||||
if app.active != nil && len(app.active) > 0 {
|
||||
song = app.active[app.plidx]
|
||||
}
|
||||
|
||||
msg["payload"] = SongInfo{
|
||||
Playlist: app.queue.Current().Playlist.Id,
|
||||
PlaylistName: app.queue.Current().Playlist.Title,
|
||||
Title: app.queue.Current().Title,
|
||||
Channel: app.queue.Current().Channel,
|
||||
Position: 0,
|
||||
Playlist: plid,
|
||||
PlaylistName: pltitle,
|
||||
Title: title,
|
||||
Channel: channel,
|
||||
Position: app.queue.Position(),
|
||||
Length: app.queue.Len(),
|
||||
Pause: !app.queue.IsPlaying(),
|
||||
Song: app.queue.Current().VideoID,
|
||||
Song: song,
|
||||
}
|
||||
|
||||
return msg
|
||||
|
@ -107,8 +128,11 @@ func (app *App) ambiancePlay(payload ...interface{}) {
|
|||
|
||||
discordspeaker.Pause(false)
|
||||
discordspeaker.Lock()
|
||||
app.ambiance.Clear()
|
||||
loop := beep.Loop(-1, play)
|
||||
if app.ambiance.IsPlaying() {
|
||||
app.ambiance.Reset()
|
||||
}
|
||||
|
||||
loop := loop.Loop(-1, play)
|
||||
app.ambiance.Add(loop)
|
||||
discordspeaker.Unlock()
|
||||
|
||||
|
@ -126,7 +150,7 @@ func (app *App) ambiancePlay(payload ...interface{}) {
|
|||
func (app *App) ambianceStop(payload ...interface{}) {
|
||||
log.Println("Stopping ambiance")
|
||||
discordspeaker.Lock()
|
||||
app.ambiance.Clear()
|
||||
app.ambiance.Reset()
|
||||
discordspeaker.Unlock()
|
||||
|
||||
msg := make(map[string]interface{})
|
||||
|
@ -187,8 +211,10 @@ func (app *App) songPosition(payload ...interface{}) {
|
|||
out := make(map[string]interface{})
|
||||
|
||||
msg["event"] = "song_position"
|
||||
if app.queue != nil {
|
||||
out["len"] = app.queue.Len()
|
||||
out["position"] = app.queue.Position()
|
||||
}
|
||||
|
||||
msg["payload"] = out
|
||||
ws_msg <- msg
|
||||
|
@ -229,7 +255,7 @@ func (app *App) checkTimeleft(payload ...interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
func (app *App) songInfo(payload ...interface{}) {
|
||||
func (app *App) songOver(payload ...interface{}) {
|
||||
log.Println("song_over event received")
|
||||
|
||||
msg := app.songInfoEvent("song_info")
|
||||
|
@ -253,8 +279,17 @@ func (app *App) stop(payload ...interface{}) {
|
|||
|
||||
func (app *App) prevSong(payload ...interface{}) {
|
||||
log.Println("prev_song event received")
|
||||
song := app.GetPrevSong(app.active)
|
||||
f, err := ffmpeg.NewPCM(song, sampleRate, channels)
|
||||
if err != nil {
|
||||
log.Println("Unable to start new ffmpeg")
|
||||
return
|
||||
}
|
||||
|
||||
app.queue.Prev()
|
||||
discordspeaker.Lock()
|
||||
app.queue.Reset()
|
||||
app.queue.Add(f)
|
||||
discordspeaker.Unlock()
|
||||
|
||||
msg := app.songInfoEvent("song_info")
|
||||
ws_msg <- msg
|
||||
|
@ -262,8 +297,17 @@ func (app *App) prevSong(payload ...interface{}) {
|
|||
|
||||
func (app *App) nextSong(payload ...interface{}) {
|
||||
log.Println("next_song event received")
|
||||
song := app.GetNextSong(app.active)
|
||||
f, err := ffmpeg.NewPCM(song, sampleRate, channels)
|
||||
if err != nil {
|
||||
log.Println("Unable to start new ffmpeg")
|
||||
return
|
||||
}
|
||||
|
||||
app.queue.Next()
|
||||
discordspeaker.Lock()
|
||||
app.queue.Reset()
|
||||
app.queue.Add(f)
|
||||
discordspeaker.Unlock()
|
||||
|
||||
msg := app.songInfoEvent("song_info")
|
||||
ws_msg <- msg
|
||||
|
@ -360,7 +404,13 @@ func (app *App) loadPlaylist(payload ...interface{}) {
|
|||
return
|
||||
}
|
||||
|
||||
app.plm.Lock()
|
||||
app.playlist = pl
|
||||
app.plm.Unlock()
|
||||
|
||||
app.next = true
|
||||
discordspeaker.Pause(false)
|
||||
|
||||
list, err := app.Playlist(pl.Url)
|
||||
if err != nil {
|
||||
log.Println("Error getting playlist info,", id)
|
||||
|
@ -373,44 +423,54 @@ func (app *App) loadPlaylist(payload ...interface{}) {
|
|||
return
|
||||
}
|
||||
|
||||
app.active = list
|
||||
|
||||
song := app.GetNextSong(list)
|
||||
f, err := ffmpeg.NewPCM(song, sampleRate, channels)
|
||||
if err != nil {
|
||||
log.Println("Unable to start new ffmpeg")
|
||||
return
|
||||
}
|
||||
|
||||
discordspeaker.Lock()
|
||||
app.queue.Reset()
|
||||
app.queue.Add(f)
|
||||
discordspeaker.Unlock()
|
||||
|
||||
go func() {
|
||||
for _, vid := range list {
|
||||
ytinfo, err := app.Video(vid)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
app.next = false
|
||||
|
||||
_, yt, err := app.cache.GetOrCreateBytes(vid+".txt", func() ([]byte, error) {
|
||||
uri, err := ytdl.NewYTdl(vid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return uri, nil
|
||||
})
|
||||
|
||||
ff, err := ffmpeg.NewPCM(string(yt), sampleRate, channels)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
song := &Song{
|
||||
Title: ytinfo.Title,
|
||||
Channel: ytinfo.Channel,
|
||||
VideoID: vid,
|
||||
Length: ytinfo.Len,
|
||||
PCM: ff,
|
||||
Playlist: *pl,
|
||||
DLuri: string(yt),
|
||||
}
|
||||
app.queue.Add(song)
|
||||
}
|
||||
}()
|
||||
msg := app.songInfoEvent("song_info")
|
||||
ws_msg <- msg
|
||||
log.Println("Added song", song)
|
||||
}
|
||||
|
||||
func (app *App) preloadSong(payload ...interface{}) {
|
||||
app.queue.Preload()
|
||||
log.Println("preload_song event received")
|
||||
app.next = true
|
||||
discordspeaker.Pause(false)
|
||||
|
||||
var song string
|
||||
switch current := app.queue.Current().(type) {
|
||||
case *ffmpeg.PCM:
|
||||
for {
|
||||
song = app.GetNextSong(app.active)
|
||||
if current.Uri != song {
|
||||
break
|
||||
}
|
||||
log.Println("Got same song, try again!")
|
||||
}
|
||||
default:
|
||||
song = app.GetNextSong(app.active)
|
||||
}
|
||||
|
||||
f, err := ffmpeg.NewPCM(song, sampleRate, channels)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
discordspeaker.Lock()
|
||||
app.queue.Add(f)
|
||||
discordspeaker.Unlock()
|
||||
|
||||
log.Println("Added song.", song)
|
||||
}
|
||||
|
|
|
@ -3,10 +3,14 @@ package ffmpeg
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"dndmusicbot/ytdl"
|
||||
)
|
||||
|
||||
type FFmpeg struct {
|
||||
|
@ -17,22 +21,46 @@ type FFmpeg struct {
|
|||
Len time.Duration
|
||||
Title string
|
||||
Channel string
|
||||
PMutex *sync.RWMutex
|
||||
ProcessState *os.ProcessState
|
||||
err chan error
|
||||
fb chan bool
|
||||
}
|
||||
|
||||
func NewFFmpeg(uri string, sampleRate int, channels int) (ff *FFmpeg, err error) {
|
||||
var yt *ytdl.YTdl
|
||||
for {
|
||||
yt, err = ytdl.NewYTdl(uri)
|
||||
if err != nil {
|
||||
log.Println("Something went wrong, trying again.", err)
|
||||
continue
|
||||
}
|
||||
if yt.Url != "" {
|
||||
break
|
||||
}
|
||||
|
||||
log.Println("Something went wrong, trying again.")
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
|
||||
RETRY:
|
||||
ff = new(FFmpeg)
|
||||
ff.PMutex = &sync.RWMutex{}
|
||||
ff.Len = yt.Len
|
||||
ff.Title = yt.Title
|
||||
ff.Channel = yt.Channel
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ff.Cancel = cancel
|
||||
|
||||
ff.Cmd = exec.CommandContext(
|
||||
ctx,
|
||||
"ffmpeg",
|
||||
"-i", uri,
|
||||
"-xerror",
|
||||
"-i", yt.Url,
|
||||
"-f", "s16le",
|
||||
"-v", "error",
|
||||
//"-stats",
|
||||
// "-stats",
|
||||
//"-progress", "pipe:2",
|
||||
"-ar", strconv.Itoa(sampleRate),
|
||||
"-ac", strconv.Itoa(channels),
|
||||
"-af", "loudnorm=I=-16:LRA=11:TP=-1.5",
|
||||
|
@ -46,22 +74,26 @@ func NewFFmpeg(uri string, sampleRate int, channels int) (ff *FFmpeg, err error)
|
|||
ff.Out = bytes.NewBuffer(make([]byte, 128*1024))
|
||||
ff.Cmd.Stdout = ff.Out
|
||||
|
||||
exitctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
go func() {
|
||||
ff.Cmd.Start()
|
||||
ff.err <- ff.Cmd.Wait()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-exitctx.Done():
|
||||
ff.Started = true
|
||||
case <-ff.err:
|
||||
log.Println("ffmpeg exited early, retry...")
|
||||
goto RETRY
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (ff *FFmpeg) Start() error {
|
||||
ff.Started = true
|
||||
err := ff.Cmd.Start()
|
||||
go func() {
|
||||
if err != nil {
|
||||
ff.err <- ff.Cmd.Wait()
|
||||
}
|
||||
}()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (ff FFmpeg) Close() error {
|
||||
func (ff *FFmpeg) Close() error {
|
||||
ff.Cancel()
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package ffmpeg
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
|
@ -21,7 +20,7 @@ type PCM struct {
|
|||
Volume float64
|
||||
}
|
||||
|
||||
func NewPCM(uri string, sampleRate int, channels int) (*PCM, error) {
|
||||
func NewPCM(uri string, sampleRate int, channels int) (beep.StreamSeekCloser, error) {
|
||||
out := new(PCM)
|
||||
|
||||
ff, err := NewFFmpeg(uri, sampleRate, channels)
|
||||
|
@ -63,7 +62,6 @@ func (d *PCM) Stream(samples [][2]float64) (n int, ok bool) {
|
|||
break
|
||||
}
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
ok = false
|
||||
break
|
||||
}
|
||||
|
|
34
go.mod
34
go.mod
|
@ -6,7 +6,6 @@ require (
|
|||
github.com/bwmarrin/discordgo v0.26.1
|
||||
github.com/dpup/gohubbub v0.0.0-20140517235056-2dc6969d22d8
|
||||
github.com/faiface/beep v1.1.0
|
||||
github.com/gohugoio/hugo v0.106.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/grafov/bcast v0.0.0-20190217190352-1447f067e08d
|
||||
|
@ -15,11 +14,8 @@ require (
|
|||
github.com/kataras/go-events v0.0.3
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/r3labs/sse/v2 v2.8.2
|
||||
github.com/sosodev/duration v1.0.1
|
||||
github.com/spf13/afero v1.9.3
|
||||
github.com/spf13/viper v1.14.0
|
||||
github.com/tidwall/gjson v1.14.3
|
||||
golang.org/x/time v0.2.0
|
||||
google.golang.org/api v0.103.0
|
||||
layeh.com/gopus v0.0.0-20210501142526-1ee02d434e32
|
||||
)
|
||||
|
@ -27,23 +23,7 @@ require (
|
|||
require (
|
||||
cloud.google.com/go/compute v1.12.1 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.1 // indirect
|
||||
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/alecthomas/chroma/v2 v2.3.0 // indirect
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
github.com/bep/clock v0.3.0 // indirect
|
||||
github.com/bep/debounce v1.2.0 // indirect
|
||||
github.com/bep/godartsass v0.14.0 // indirect
|
||||
github.com/bep/golibsass v1.1.0 // indirect
|
||||
github.com/bep/overlayfs v0.6.0 // indirect
|
||||
github.com/clbanning/mxj/v2 v2.5.7 // indirect
|
||||
github.com/cli/safeexec v1.0.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/gohugoio/locales v0.14.0 // indirect
|
||||
github.com/gohugoio/localescompressed v1.0.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect
|
||||
|
@ -52,32 +32,24 @@ require (
|
|||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||
github.com/jdkato/prose v1.2.1 // indirect
|
||||
github.com/kyokomi/emoji/v2 v2.2.10 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/mitchellh/hashstructure v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/niklasfasching/go-org v1.6.5 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
||||
github.com/spf13/afero v1.9.2 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.4.1 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.6.4 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/yuin/goldmark v1.5.3 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/crypto v0.2.0 // indirect
|
||||
golang.org/x/net v0.2.0 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
|
||||
golang.org/x/sys v0.2.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/time v0.2.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect
|
||||
google.golang.org/grpc v1.50.1 // indirect
|
||||
|
|
118
go.sum
118
go.sum
|
@ -42,58 +42,23 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
|
|||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69 h1:+tu3HOoMXB7RXEINRVIpxJCT+KdYiI7LAEAUrOw3dIU=
|
||||
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZdiDllfyYH5l5OkAaZtk7VkWe89bPJFmnDBNHxg=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/alecthomas/chroma/v2 v2.3.0 h1:83xfxrnjv8eK+Cf8qZDzNo3PPF9IbTWHs7z28GY6D0U=
|
||||
github.com/alecthomas/chroma/v2 v2.3.0/go.mod h1:mZxeWZlxP2Dy+/8cBob2PYd8O2DwNAzave5AY7A2eQw=
|
||||
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
|
||||
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/bep/clock v0.3.0 h1:vfOA6+wVb6pPQEiXow9f/too92vNTLe9MuwO13PfI0M=
|
||||
github.com/bep/clock v0.3.0/go.mod h1:6Gz2lapnJ9vxpvPxQ2u6FcXFRoj4kkiqQ6pm0ERZlwk=
|
||||
github.com/bep/debounce v1.2.0 h1:wXds8Kq8qRfwAOpAxHrJDbCXgC5aHSzgQb/0gKsHQqo=
|
||||
github.com/bep/debounce v1.2.0/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
||||
github.com/bep/gitmap v1.1.2 h1:zk04w1qc1COTZPPYWDQHvns3y1afOsdRfraFQ3qI840=
|
||||
github.com/bep/goat v0.5.0 h1:S8jLXHCVy/EHIoCY+btKkmcxcXFd34a0Q63/0D4TKeA=
|
||||
github.com/bep/godartsass v0.14.0 h1:pPb6XkpyDEppS+wK0veh7OXDQc4xzOJI9Qcjb743UeQ=
|
||||
github.com/bep/godartsass v0.14.0/go.mod h1:6LvK9RftsXMxGfsA0LDV12AGc4Jylnu6NgHL+Q5/pE8=
|
||||
github.com/bep/golibsass v1.1.0 h1:pjtXr00IJZZaOdfryNa9wARTB3Q0BmxC3/V1KNcgyTw=
|
||||
github.com/bep/golibsass v1.1.0/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA=
|
||||
github.com/bep/gowebp v0.2.0 h1:ZVfK8i9PpZqKHEmthQSt3qCnnHycbLzBPEsVtk2ch2Q=
|
||||
github.com/bep/overlayfs v0.6.0 h1:sgLcq/qtIzbaQNl2TldGXOkHvqeZB025sPvHOQL+DYo=
|
||||
github.com/bep/overlayfs v0.6.0/go.mod h1:NFjSmn3kCqG7KX2Lmz8qT8VhPPCwZap3UNogXawoQHM=
|
||||
github.com/bep/tmc v0.5.1 h1:CsQnSC6MsomH64gw0cT5f+EwQDcvZz4AazKunFwTpuI=
|
||||
github.com/bep/workers v1.0.0 h1:U+H8YmEaBCEaFZBst7GcRVEoqeRC9dzH2dWOwGmOchg=
|
||||
github.com/bep/workers v1.0.0/go.mod h1:7kIESOB86HfR2379pwoMWNy8B50D7r99fRLUyPSNyCs=
|
||||
github.com/bwmarrin/discordgo v0.26.1 h1:AIrM+g3cl+iYBr4yBxCBp9tD9jR3K7upEjl0d89FRkE=
|
||||
github.com/bwmarrin/discordgo v0.26.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/clbanning/mxj/v2 v2.5.7 h1:7q5lvUpaPF/WOkqgIDiwjBJaznaLCCBd78pi8ZyAnE0=
|
||||
github.com/clbanning/mxj/v2 v2.5.7/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI=
|
||||
github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc=
|
||||
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dpup/gohubbub v0.0.0-20140517235056-2dc6969d22d8 h1:t1Ox7k2+GSzIv3fihjV7YFGb40nb/e2oyrTM/ngbzbA=
|
||||
github.com/dpup/gohubbub v0.0.0-20140517235056-2dc6969d22d8/go.mod h1:QqXVl9BAyVoWIZE4oA9XfkwCjQ3JaajiX4vq7Zh8Vzs=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
|
@ -102,37 +67,19 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
|||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanw/esbuild v0.15.14 h1:J/cqgL3yfj/HDHDo9txKAqyzTBYfAMuqCknkS2jhX24=
|
||||
github.com/faiface/beep v1.1.0 h1:A2gWP6xf5Rh7RG/p9/VAW2jRSDEGQm5sbOb38sf5d4c=
|
||||
github.com/faiface/beep v1.1.0/go.mod h1:6I8p6kK2q4opL/eWb+kAkk38ehnTunWeToJB+s51sT4=
|
||||
github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
|
||||
github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU=
|
||||
github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
|
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
|
||||
github.com/getkin/kin-openapi v0.108.0 h1:EYf0GtsKa4hQNIlplGS+Au7NEfGQ1F7MoHD2kcVevPQ=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs=
|
||||
github.com/go-audio/riff v1.0.0/go.mod h1:l3cQwc85y79NQFCRB7TiPoNiaijp6q8Z0Uv38rVG498=
|
||||
github.com/go-audio/wav v1.0.0/go.mod h1:3yoReyQOsiARkvPl3ERCi8JFjihzG6WhjYpZCf5zAWE=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
||||
github.com/gobuffalo/flect v0.3.0 h1:erfPWM+K1rFNIQeRPdeEXxo8yFr/PO17lhRnS8FUrtk=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20210430103248-4c28c89f8013 h1:Nj29Qbkt0bZ/bJl8eccfxQp3NlU/0IW1v9eyYtQ53XQ=
|
||||
github.com/gohugoio/hugo v0.106.0 h1:MDTmX2l1/zTh0HS4CADta4a/b63aiyj6yC2WW4A+UR0=
|
||||
github.com/gohugoio/hugo v0.106.0/go.mod h1:eBHtMtZZrZweoC65GRsAM+jcYhHGmbzzvSccapxL4ug=
|
||||
github.com/gohugoio/locales v0.14.0 h1:Q0gpsZwfv7ATHMbcTNepFd59H7GoykzWJIxi113XGDc=
|
||||
github.com/gohugoio/locales v0.14.0/go.mod h1:ip8cCAv/cnmVLzzXtiTpPwgJ4xhKZranqNqtoIu0b/4=
|
||||
github.com/gohugoio/localescompressed v1.0.1 h1:KTYMi8fCWYLswFyJAeOtuk/EkXR/KPTHHNN9OS+RTxo=
|
||||
github.com/gohugoio/localescompressed v1.0.1/go.mod h1:jBF6q8D7a0vaEmcWPNcAjUZLJaIVNiwvM3WlmTvooB0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
@ -176,7 +123,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
|
@ -207,7 +153,6 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
|
|||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grafov/bcast v0.0.0-20190217190352-1447f067e08d h1:Q2+KsA/1GLC9xyLsDun3/EOJ+83rY/IHRsO1DToPrdo=
|
||||
github.com/grafov/bcast v0.0.0-20190217190352-1447f067e08d/go.mod h1:RInr+B3/Tx70hYm0rpNPMTD7vH0pBG5ny/JsHAs2KcQ=
|
||||
github.com/hairyhenderson/go-codeowners v0.2.3-0.20201026200250-cdc7c0759690 h1:XWjCrg/HJRLZCbvsUxS5R/9JhwiiwNctEsRvZ1Vjz5k=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.0 h1:fTM5DXjp/DL2G74HHAs/aBGiS9Tg7wnp+jkU38bHy4g=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.0/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
|
||||
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
|
||||
|
@ -220,15 +165,12 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
|
|||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/icza/bitio v1.0.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A=
|
||||
github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA=
|
||||
github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgx/v5 v5.1.0 h1:Z7pLKUb65HK6m18No8GGKT87K34NhIIEHa86rRdjxbU=
|
||||
github.com/jackc/pgx/v5 v5.1.0/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk=
|
||||
github.com/jdkato/prose v1.2.1 h1:Fp3UnJmLVISmlc57BgKUzdjr0lOtjqTZicL3PaYy6cU=
|
||||
github.com/jdkato/prose v1.2.1/go.mod h1:AiRHgVagnEx2JbQRQowVBKjG0bcs/vtkGCH1dYAL1rA=
|
||||
github.com/jfreymuth/oggvorbis v1.0.1/go.mod h1:NqS+K+UXKje0FUYUPosyQ+XTVvjmVjps1aEZH1sumIk=
|
||||
github.com/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
|
@ -240,43 +182,22 @@ github.com/kataras/go-events v0.0.3/go.mod h1:bFBgtzwwzrag7kQmGuU1ZaVxhK2qseYPQo
|
|||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kyokomi/emoji/v2 v2.2.10 h1:1z5eMVcxFifsmEoNpdeq4UahbcicgQ4FEHuzrCVwmiI=
|
||||
github.com/kyokomi/emoji/v2 v2.2.10/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
|
||||
github.com/marekm4/color-extractor v1.2.0 h1:DCU/FXg3PlAwig7W5PRZshiX5x38k0aNPTxYZ6/fZb0=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mewkiz/flac v1.0.7/go.mod h1:yU74UH277dBUpqxPouHSQIar3G1X/QIclVbFahSd1pU=
|
||||
github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2/go.mod h1:3E2FUC/qYUfM8+r9zAwpeHJzqRVVMIYnpzD/clwWxyA=
|
||||
github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0=
|
||||
github.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/6d8ulp4AwfLKrmA=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/montanaflynn/stats v0.6.3/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/muesli/smartcrop v0.3.0 h1:JTlSkmxWg/oQ1TcLDoypuirdE8Y/jzNirQeLkxpA6Oc=
|
||||
github.com/neurosnap/sentences v1.0.6/go.mod h1:pg1IapvYpWCJJm/Etxeh0+gtMf1rI1STY9S7eUCPbDc=
|
||||
github.com/niklasfasching/go-org v1.6.5 h1:5YAIqNTdl6lAOb7lD2AyQ1RuFGPVrAKvUexphk8PGbo=
|
||||
github.com/niklasfasching/go-org v1.6.5/go.mod h1:ybv0eGDnxylFUfFE+ySaQc734j/L3+/ChKZ/h63a2wM=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
|
||||
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
@ -287,16 +208,9 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
|
|||
github.com/r3labs/sse/v2 v2.8.2 h1:YWZy2i2nLoD5fE3vLLTdTz/8wxIYIFp5XbLNmmrrNts=
|
||||
github.com/r3labs/sse/v2 v2.8.2/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktEmkNJ7I=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
|
||||
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||
github.com/shogo82148/go-shuffle v0.0.0-20180218125048-27e6095f230d/go.mod h1:2htx6lmL0NGLHlO8ZCf+lQBGBHIbEujyywxJArf+2Yc=
|
||||
github.com/sosodev/duration v1.0.1 h1:qovz/BFb6kp30KZ4/AYZvB5Z6zANmeQja5l6W9X1w68=
|
||||
github.com/sosodev/duration v1.0.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
|
||||
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
|
||||
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw=
|
||||
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
|
@ -312,7 +226,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
|||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
|
@ -320,11 +233,6 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs
|
|||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
||||
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/tdewolff/minify/v2 v2.12.4 h1:kejsHQMM17n6/gwdw53qsi6lg0TGddZADVyQOz1KMdE=
|
||||
github.com/tdewolff/parse/v2 v2.6.4 h1:KCkDvNUMof10e3QExio9OPZJT8SbdKojLBumw8YZycQ=
|
||||
github.com/tdewolff/parse/v2 v2.6.4/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs=
|
||||
github.com/tdewolff/test v1.0.7 h1:8Vs0142DmPFW/bQeHRP3MV19m1gvndjUb1sn8yy74LM=
|
||||
github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
|
||||
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
|
@ -336,9 +244,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.5.3 h1:3HUJmBFbQW9fhQOzMgseU134xfi6hU+mjWywx5Ty+/M=
|
||||
github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
|
@ -347,7 +252,6 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
|
@ -370,7 +274,6 @@ golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMk
|
|||
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
|
@ -393,7 +296,6 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -427,7 +329,6 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
|
@ -451,8 +352,6 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -487,11 +386,8 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -502,7 +398,6 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
|
|||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
@ -557,8 +452,6 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f
|
|||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -673,7 +566,6 @@ gopkg.in/fatih/set.v0 v0.2.1 h1:Xvyyp7LXu34P0ROhCyfXkmQCAoOUKb1E2JS9I7SE5CY=
|
|||
gopkg.in/fatih/set.v0 v0.2.1/go.mod h1:5eLWEndGL4zGGemXWrKuts+wTJR0y+w+auqUJZbmyBg=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/neurosnap/sentences.v1 v1.0.6/go.mod h1:YlK+SN+fLQZj+kY3r8DkGDhDr91+S3JmTb5LSxFRQo0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
|
|
131
queue.go
131
queue.go
|
@ -1,14 +1,11 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/faiface/beep"
|
||||
"github.com/kataras/go-events"
|
||||
|
||||
"dndmusicbot/ffmpeg"
|
||||
discordspeaker "dndmusicbot/speaker"
|
||||
)
|
||||
|
||||
|
@ -18,130 +15,68 @@ type Ambiance struct {
|
|||
}
|
||||
|
||||
func init() {
|
||||
app.ambiance = beep.Mixer{}
|
||||
discordspeaker.Play(&app.ambiance)
|
||||
app.ambiance = new(Queue)
|
||||
app.ambiance.Events = app.events
|
||||
discordspeaker.Play(app.ambiance)
|
||||
|
||||
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 {
|
||||
streamers []beep.StreamSeekCloser
|
||||
Events events.EventEmmiter
|
||||
playing bool
|
||||
yt VideoInfo
|
||||
list *list.List
|
||||
current *list.Element
|
||||
}
|
||||
|
||||
func (q Queue) IsPlaying() bool {
|
||||
return q.playing
|
||||
}
|
||||
|
||||
func (q *Queue) Play() {
|
||||
func (q *Queue) Add(streamers ...beep.StreamSeekCloser) {
|
||||
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()
|
||||
}
|
||||
q.streamers = append(q.streamers, streamers...)
|
||||
}
|
||||
|
||||
func (q Queue) QLen() int {
|
||||
return q.list.Len()
|
||||
return len(q.streamers)
|
||||
}
|
||||
|
||||
func (q Queue) Current() *Song {
|
||||
if q.current == nil {
|
||||
return new(Song)
|
||||
func (q Queue) Len() int {
|
||||
if len(q.streamers) > 0 {
|
||||
return q.streamers[0].Len()
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (q Queue) Current() beep.StreamSeekCloser {
|
||||
if len(q.streamers) > 0 {
|
||||
return q.streamers[0]
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
return q.current.Value.(*Song)
|
||||
}
|
||||
|
||||
func (q *Queue) Reset() {
|
||||
q.playing = false
|
||||
|
||||
q.current = nil
|
||||
q.list = q.list.Init()
|
||||
}
|
||||
|
||||
func (q *Queue) Next() {
|
||||
if q.current == nil {
|
||||
return
|
||||
for _, stream := range q.streamers {
|
||||
stream.Close()
|
||||
}
|
||||
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
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (q *Queue) Preload() {
|
||||
q.streamers = nil
|
||||
}
|
||||
|
||||
func (q *Queue) Stream(samples [][2]float64) (n int, ok bool) {
|
||||
if q.current != nil && !q.current.Value.(*Song).PCM.Player.Started {
|
||||
q.current.Value.(*Song).PCM.Player.Start()
|
||||
}
|
||||
// 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 {
|
||||
if len(q.streamers) == 0 {
|
||||
for i := range samples[filled:] {
|
||||
samples[i][0] = 0
|
||||
samples[i][1] = 0
|
||||
|
@ -150,11 +85,11 @@ func (q *Queue) Stream(samples [][2]float64) (n int, ok bool) {
|
|||
}
|
||||
|
||||
// We stream from the first streamer in the queue.
|
||||
n, ok := q.current.Value.(*Song).PCM.Stream(samples[filled:])
|
||||
n, ok := q.streamers[0].Stream(samples[filled:])
|
||||
// If it's drained, we pop it from the queue, thus continuing with
|
||||
// the next streamer.
|
||||
if !ok {
|
||||
q.Next()
|
||||
q.streamers = q.streamers[1:]
|
||||
q.Events.Emit("song_over", nil)
|
||||
}
|
||||
// We update the number of filled samples.
|
||||
|
@ -168,17 +103,9 @@ func (q *Queue) Err() error {
|
|||
}
|
||||
|
||||
func (q *Queue) Position() int {
|
||||
if q.current == nil || q.current.Value.(*Song).PCM == nil {
|
||||
if len(q.streamers) > 0 {
|
||||
return q.streamers[0].Position()
|
||||
} else {
|
||||
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())
|
||||
}
|
||||
|
|
2
ws.go
2
ws.go
|
@ -70,7 +70,7 @@ func handleWS(c *websocket.Conn) error {
|
|||
c.SetWriteDeadline(time.Now().Add(10 * time.Second))
|
||||
c.WriteJSON(msg)
|
||||
|
||||
if app.ambiance.Len() > 0 {
|
||||
if app.ambiance.IsPlaying() {
|
||||
msg := make(map[string]interface{})
|
||||
out := make(map[string]interface{})
|
||||
msg["event"] = "ambiance_play"
|
||||
|
|
62
youtube.go
62
youtube.go
|
@ -1,12 +1,10 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math/big"
|
||||
|
@ -15,7 +13,6 @@ import (
|
|||
mrand "math/rand"
|
||||
|
||||
"github.com/faiface/beep"
|
||||
"github.com/sosodev/duration"
|
||||
"google.golang.org/api/option"
|
||||
"google.golang.org/api/youtube/v3"
|
||||
)
|
||||
|
@ -60,7 +57,7 @@ func ShufflePlaylist(list []string) ([]string, error) {
|
|||
}
|
||||
|
||||
func (app *App) GetSong(list []string) string {
|
||||
return list[app.plidx]
|
||||
return fmt.Sprintf(yt_url, list[app.plidx])
|
||||
}
|
||||
|
||||
func (app *App) GetNextSong(list []string) string {
|
||||
|
@ -68,7 +65,7 @@ func (app *App) GetNextSong(list []string) string {
|
|||
if app.plidx >= len(app.active) {
|
||||
app.plidx = 0
|
||||
}
|
||||
return list[app.plidx]
|
||||
return fmt.Sprintf(yt_url, list[app.plidx])
|
||||
}
|
||||
|
||||
func (app *App) GetPrevSong(list []string) string {
|
||||
|
@ -76,7 +73,7 @@ func (app *App) GetPrevSong(list []string) string {
|
|||
if app.plidx < 0 {
|
||||
app.plidx = len(list)
|
||||
}
|
||||
return list[app.plidx]
|
||||
return fmt.Sprintf(yt_url, list[app.plidx])
|
||||
}
|
||||
|
||||
func GetRandomSong(list []string) string {
|
||||
|
@ -89,56 +86,7 @@ func GetRandomSong(list []string) string {
|
|||
log.Println("Failed to get random int, ", err)
|
||||
return ""
|
||||
}
|
||||
return list[idx.Int64()]
|
||||
}
|
||||
|
||||
type VideoInfo struct {
|
||||
Title string
|
||||
Channel string
|
||||
Len time.Duration
|
||||
Uri string
|
||||
}
|
||||
|
||||
func (app App) Video(vid string) (out VideoInfo, err error) {
|
||||
_, r, err := app.cache.GetOrCreate(vid+".videoinfo", func() (io.ReadCloser, error) {
|
||||
call := app.youtube.Videos.List([]string{"snippet", "contentDetails"})
|
||||
call.MaxResults(1)
|
||||
call.Id(vid)
|
||||
resp, err := call.Do()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(resp.Items) != 1 {
|
||||
return nil, errors.New("Response contains not 1 item!")
|
||||
}
|
||||
|
||||
video := resp.Items[0]
|
||||
|
||||
songdur, err := duration.Parse(video.ContentDetails.Duration)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := new(VideoInfo)
|
||||
out.Len = songdur.ToTimeDuration()
|
||||
out.Title = video.Snippet.Title
|
||||
out.Channel = video.Snippet.ChannelTitle
|
||||
out.Uri = vid
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
json.NewEncoder(&buf).Encode(out)
|
||||
|
||||
return io.NopCloser(&buf), nil
|
||||
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
json.NewDecoder(r).Decode(&out)
|
||||
|
||||
return out, nil
|
||||
return fmt.Sprintf(yt_url, list[idx.Int64()])
|
||||
}
|
||||
|
||||
func (app App) Playlist(playlist string) ([]string, error) {
|
||||
|
|
49
ytdl/ytdl.go
49
ytdl/ytdl.go
|
@ -1,15 +1,38 @@
|
|||
package ytdl
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/cache/filecache"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
var cache *filecache.Cache
|
||||
var yturl = "https://youtu.be/%s"
|
||||
|
||||
func NewYTdl(vid string) ([]byte, error) {
|
||||
uri, err := exec.Command(
|
||||
func init() {
|
||||
fs := afero.NewMemMapFs()
|
||||
cache = filecache.NewCache(fs, 6*time.Hour, "")
|
||||
}
|
||||
|
||||
type YTdl struct {
|
||||
Title string
|
||||
Url string
|
||||
Channel string
|
||||
Len time.Duration
|
||||
}
|
||||
|
||||
func NewYTdl(vid string) (*YTdl, error) {
|
||||
log.Printf("Loading %s from youtube\n", vid)
|
||||
_, ytdl_js, err := cache.GetOrCreateBytes(vid+".json", func() ([]byte, error) {
|
||||
log.Printf("%s not found in cache, downloading info.\n", vid)
|
||||
js, err := exec.Command(
|
||||
"./bin/yt-dlp_linux",
|
||||
fmt.Sprintf(yturl, vid),
|
||||
"--cookies", "./cookies.txt",
|
||||
|
@ -19,13 +42,31 @@ func NewYTdl(vid string) ([]byte, error) {
|
|||
"--newline",
|
||||
"--restrict-filenames",
|
||||
"-f", "140",
|
||||
"--get-url",
|
||||
"-j",
|
||||
).Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return uri, nil
|
||||
log.Printf("%s is now cached.\n", vid)
|
||||
|
||||
return js, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !gjson.ValidBytes(ytdl_js) {
|
||||
return nil, errors.New("invalid json")
|
||||
}
|
||||
|
||||
results := gjson.GetManyBytes(ytdl_js, "title", "url", "duration", "channel")
|
||||
title := results[0].String()
|
||||
geturl := results[1].String()
|
||||
duration, err := time.ParseDuration(fmt.Sprintf("%ds", results[2].Int()))
|
||||
channel := results[3].String()
|
||||
|
||||
return &YTdl{title, geturl, channel, duration}, nil
|
||||
}
|
||||
|
||||
func DownloadAmbiance(uri string, name string) error {
|
||||
|
|
Loading…
Reference in New Issue