Compare commits
5 Commits
48677ffe44
...
ec7900ff63
Author | SHA1 | Date |
---|---|---|
Trond Ekseth | ec7900ff63 | |
Trond Ekseth | a091df36e4 | |
Trond Ekseth | ceebf7c099 | |
Stein Ivar Berghei | e79f5aee67 | |
Stein Ivar Berghei | b493db9592 |
|
@ -7,6 +7,11 @@ cookies.txt
|
|||
config
|
||||
css/*.min.css
|
||||
js/*.min.js
|
||||
cache
|
||||
|
||||
bin
|
||||
ambiance/*.mp3
|
||||
|
||||
*.yml
|
||||
public/
|
||||
node_modules/
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# docker build -t dndmusicbot-js-build .
|
||||
# docker run -it -v $(pwd)/public:/app/public dndmusicbot-js-build
|
||||
FROM node:19-alpine3.16
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . /app
|
||||
|
||||
RUN yarn
|
||||
|
||||
ENTRYPOINT ["yarn", "build"]
|
9
bot.go
9
bot.go
|
@ -11,10 +11,13 @@ 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"
|
||||
)
|
||||
|
@ -48,7 +51,7 @@ type App struct {
|
|||
voice *discordgo.VoiceConnection
|
||||
youtube *youtube.Service
|
||||
queue *Queue
|
||||
ambiance *Queue
|
||||
ambiance beep.Mixer
|
||||
curamb string
|
||||
events events.EventEmmiter
|
||||
next bool
|
||||
|
@ -60,9 +63,13 @@ 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)
|
||||
|
||||
|
|
138
css/style.css
138
css/style.css
|
@ -1,41 +1,69 @@
|
|||
.container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-template-rows: auto;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
--grid-layout-gap: 8px;
|
||||
--grid-column-count: 3;
|
||||
--grid-item--min-width: 150px;
|
||||
|
||||
--gap-count: calc(var(--grid-column-count) - 1);
|
||||
--total-gap-width: calc(var(--gap-count) * var(--grid-layout-gap));
|
||||
--grid-item--max-width: calc((100% - var(--total-gap-width)) / var(--grid-column-count));
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(max(var(--grid-item--min-width), var(--grid-item--max-width)), 1fr));
|
||||
grid-auto-rows: 1fr;
|
||||
grid-gap: var(--grid-layout-gap);
|
||||
}
|
||||
|
||||
.playing {
|
||||
background-color: burlywood;
|
||||
background-color: burlywood;
|
||||
}
|
||||
|
||||
h2.bot {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 0px;
|
||||
text-align: center;
|
||||
margin-top: 0;
|
||||
margin-bottom: .5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.item {
|
||||
background-color: #80cbc4;
|
||||
border: 1px solid black;
|
||||
padding: 20px;
|
||||
font-size: clamp(1rem, -0.8750rem + 8.3333vw, 2rem);
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
color: #073642
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
background-color: #80cbc4;
|
||||
border: 1px solid black;
|
||||
font-size: clamp(1rem, -0.8750rem + 8.3333vw, 2rem);
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
color: #073642
|
||||
}
|
||||
|
||||
.container2 {
|
||||
display: block;
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
.item.stop {
|
||||
background-color: #cb4b16;
|
||||
}
|
||||
|
||||
#info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
max-width: 512px;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
max-width: 512px;
|
||||
}
|
||||
|
||||
.box {
|
||||
/* background-color: #fefefe;*/
|
||||
/* background-color: #fefefe;*/
|
||||
margin: 15% auto; /* 15% from the top and centered */
|
||||
padding: 20px;
|
||||
padding: 2rem;
|
||||
/*border: 1px solid #888;*/
|
||||
width: 80%; /* Could be more or less, depending on screen size */
|
||||
align-content: center;
|
||||
|
@ -58,12 +86,72 @@ h2.bot {
|
|||
/* Modal Content/Box */
|
||||
.modal-content {
|
||||
margin: 15% auto; /* 15% from the top and centered */
|
||||
padding: 20px;
|
||||
padding: 2rem;
|
||||
width: 80%; /* Could be more or less, depending on screen size */
|
||||
text-align: center;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
body > div.container2 > input[type=text]:nth-child(2) {
|
||||
width: 512px;
|
||||
body, html {
|
||||
margin: 0;
|
||||
padding: .5rem;
|
||||
}
|
||||
|
||||
input[type="button"],
|
||||
input[type="submit"] {
|
||||
display: inline-block;
|
||||
height: 38px;
|
||||
padding: 0 30px;
|
||||
color: #555;
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
line-height: 38px;
|
||||
letter-spacing: .1rem;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #bbb;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input[type="button"]:focus,
|
||||
input[type="submit"]:hover {
|
||||
color: #333;
|
||||
border-color: #888;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
height: 38px;
|
||||
padding: 6px 10px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #D1D1D1;
|
||||
border-radius: 4px;
|
||||
box-shadow: none;
|
||||
box-sizing: border-box;
|
||||
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
input[type="text"]:focus {
|
||||
border: 1px solid #33C3F0;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
section {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
.u-full-width {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.u-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
|
176
events.go
176
events.go
|
@ -3,7 +3,6 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"dndmusicbot/ffmpeg"
|
||||
"dndmusicbot/loop"
|
||||
discordspeaker "dndmusicbot/speaker"
|
||||
"dndmusicbot/ytdl"
|
||||
"encoding/json"
|
||||
|
@ -13,6 +12,7 @@ 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"`
|
||||
PlaylistName string `json:"playlistname"`
|
||||
Title string `json:"current"`
|
||||
Channel string `json:"channel"`
|
||||
Playlist uuid.UUID `json:"playlist,omitempty"`
|
||||
PlaylistName string `json:"playlistname,omitempty"`
|
||||
Title string `json:"current,omitempty"`
|
||||
Channel string `json:"channel,omitempty"`
|
||||
Position int `json:"position"`
|
||||
Length int `json:"len"`
|
||||
Length int `json:"len,omitempty"`
|
||||
Pause bool `json:"pause"`
|
||||
Song string `json:"song"`
|
||||
Song string `json:"song,omitempty"`
|
||||
}
|
||||
|
||||
var l = rate.Sometimes{Interval: 1 * time.Second}
|
||||
|
@ -39,8 +39,9 @@ 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.songOver)
|
||||
//app.events.On("preload_song", app.preloadSong)
|
||||
app.events.On("song_over", app.songInfo)
|
||||
app.events.On("song_start", app.songInfo)
|
||||
//app.events.On("song_position", app.songPosition)
|
||||
|
||||
app.events.On("ambiance_play", app.ambiancePlay)
|
||||
|
@ -60,37 +61,15 @@ 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: plid,
|
||||
PlaylistName: pltitle,
|
||||
Title: title,
|
||||
Channel: channel,
|
||||
Position: app.queue.Position(),
|
||||
Playlist: app.queue.Current().Playlist.Id,
|
||||
PlaylistName: app.queue.Current().Playlist.Title,
|
||||
Title: app.queue.Current().Title,
|
||||
Channel: app.queue.Current().Channel,
|
||||
Position: 0,
|
||||
Length: app.queue.Len(),
|
||||
Pause: !app.queue.IsPlaying(),
|
||||
Song: song,
|
||||
Song: app.queue.Current().VideoID,
|
||||
}
|
||||
|
||||
return msg
|
||||
|
@ -128,11 +107,8 @@ func (app *App) ambiancePlay(payload ...interface{}) {
|
|||
|
||||
discordspeaker.Pause(false)
|
||||
discordspeaker.Lock()
|
||||
if app.ambiance.IsPlaying() {
|
||||
app.ambiance.Reset()
|
||||
}
|
||||
|
||||
loop := loop.Loop(-1, play)
|
||||
app.ambiance.Clear()
|
||||
loop := beep.Loop(-1, play)
|
||||
app.ambiance.Add(loop)
|
||||
discordspeaker.Unlock()
|
||||
|
||||
|
@ -150,7 +126,7 @@ func (app *App) ambiancePlay(payload ...interface{}) {
|
|||
func (app *App) ambianceStop(payload ...interface{}) {
|
||||
log.Println("Stopping ambiance")
|
||||
discordspeaker.Lock()
|
||||
app.ambiance.Reset()
|
||||
app.ambiance.Clear()
|
||||
discordspeaker.Unlock()
|
||||
|
||||
msg := make(map[string]interface{})
|
||||
|
@ -211,10 +187,8 @@ 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()
|
||||
}
|
||||
out["len"] = app.queue.Len()
|
||||
out["position"] = app.queue.Position()
|
||||
|
||||
msg["payload"] = out
|
||||
ws_msg <- msg
|
||||
|
@ -255,7 +229,7 @@ func (app *App) checkTimeleft(payload ...interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
func (app *App) songOver(payload ...interface{}) {
|
||||
func (app *App) songInfo(payload ...interface{}) {
|
||||
log.Println("song_over event received")
|
||||
|
||||
msg := app.songInfoEvent("song_info")
|
||||
|
@ -279,17 +253,8 @@ 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
|
||||
}
|
||||
|
||||
discordspeaker.Lock()
|
||||
app.queue.Reset()
|
||||
app.queue.Add(f)
|
||||
discordspeaker.Unlock()
|
||||
app.queue.Prev()
|
||||
|
||||
msg := app.songInfoEvent("song_info")
|
||||
ws_msg <- msg
|
||||
|
@ -297,17 +262,8 @@ 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
|
||||
}
|
||||
|
||||
discordspeaker.Lock()
|
||||
app.queue.Reset()
|
||||
app.queue.Add(f)
|
||||
discordspeaker.Unlock()
|
||||
app.queue.Next()
|
||||
|
||||
msg := app.songInfoEvent("song_info")
|
||||
ws_msg <- msg
|
||||
|
@ -404,13 +360,7 @@ 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)
|
||||
|
@ -423,54 +373,44 @@ 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()
|
||||
|
||||
app.next = false
|
||||
go func() {
|
||||
for _, vid := range list {
|
||||
ytinfo, err := app.Video(vid)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
msg := app.songInfoEvent("song_info")
|
||||
ws_msg <- msg
|
||||
log.Println("Added song", song)
|
||||
_, 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)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (app *App) preloadSong(payload ...interface{}) {
|
||||
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)
|
||||
app.queue.Preload()
|
||||
}
|
||||
|
|
|
@ -3,64 +3,36 @@ package ffmpeg
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"dndmusicbot/ytdl"
|
||||
)
|
||||
|
||||
type FFmpeg struct {
|
||||
Out *bytes.Buffer
|
||||
Cmd *exec.Cmd
|
||||
Started bool
|
||||
Cancel context.CancelFunc
|
||||
Len time.Duration
|
||||
Title string
|
||||
Channel string
|
||||
PMutex *sync.RWMutex
|
||||
ProcessState *os.ProcessState
|
||||
err chan error
|
||||
fb chan bool
|
||||
Out *bytes.Buffer
|
||||
Cmd *exec.Cmd
|
||||
Started bool
|
||||
Cancel context.CancelFunc
|
||||
Len time.Duration
|
||||
Title string
|
||||
Channel string
|
||||
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",
|
||||
"-xerror",
|
||||
"-i", yt.Url,
|
||||
"-i", uri,
|
||||
"-f", "s16le",
|
||||
"-v", "error",
|
||||
// "-stats",
|
||||
//"-progress", "pipe:2",
|
||||
//"-stats",
|
||||
"-ar", strconv.Itoa(sampleRate),
|
||||
"-ac", strconv.Itoa(channels),
|
||||
"-af", "loudnorm=I=-16:LRA=11:TP=-1.5",
|
||||
|
@ -74,26 +46,22 @@ RETRY:
|
|||
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) Close() error {
|
||||
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 {
|
||||
ff.Cancel()
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package ffmpeg
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
|
@ -20,7 +21,7 @@ type PCM struct {
|
|||
Volume float64
|
||||
}
|
||||
|
||||
func NewPCM(uri string, sampleRate int, channels int) (beep.StreamSeekCloser, error) {
|
||||
func NewPCM(uri string, sampleRate int, channels int) (*PCM, error) {
|
||||
out := new(PCM)
|
||||
|
||||
ff, err := NewFFmpeg(uri, sampleRate, channels)
|
||||
|
@ -62,6 +63,7 @@ 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,6 +6,7 @@ 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
|
||||
|
@ -14,8 +15,11 @@ 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
|
||||
)
|
||||
|
@ -23,7 +27,23 @@ 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
|
||||
|
@ -32,24 +52,32 @@ 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.5 // indirect
|
||||
github.com/spf13/afero v1.9.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||
github.com/rogpeppe/go-internal v1.9.0 // 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,23 +42,58 @@ 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=
|
||||
|
@ -67,19 +102,37 @@ 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=
|
||||
|
@ -123,6 +176,7 @@ 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=
|
||||
|
@ -153,6 +207,7 @@ 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=
|
||||
|
@ -165,12 +220,15 @@ 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=
|
||||
|
@ -182,22 +240,43 @@ 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.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
|
||||
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/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=
|
||||
|
@ -208,9 +287,16 @@ 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 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/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/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=
|
||||
|
@ -226,6 +312,7 @@ 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=
|
||||
|
@ -233,6 +320,11 @@ 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=
|
||||
|
@ -244,6 +336,9 @@ 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=
|
||||
|
@ -252,6 +347,7 @@ 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=
|
||||
|
@ -274,6 +370,7 @@ 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=
|
||||
|
@ -296,6 +393,7 @@ 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=
|
||||
|
@ -329,6 +427,7 @@ 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=
|
||||
|
@ -352,6 +451,8 @@ 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=
|
||||
|
@ -386,8 +487,11 @@ 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=
|
||||
|
@ -398,6 +502,7 @@ 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=
|
||||
|
@ -452,6 +557,8 @@ 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=
|
||||
|
@ -566,6 +673,7 @@ 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=
|
||||
|
|
35
js/script.js
35
js/script.js
|
@ -1,4 +1,7 @@
|
|||
window.onload = function() {
|
||||
import ReconnectingWebSocket from 'reconnecting-websocket';
|
||||
import Sortable from 'sortablejs';
|
||||
|
||||
window.onload = function () {
|
||||
const ws = new ReconnectingWebSocket(((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "/ws");
|
||||
|
||||
const items = document.querySelector("#items");
|
||||
|
@ -6,6 +9,8 @@ window.onload = function() {
|
|||
const submit = document.querySelector("#addplaylist")
|
||||
const output = document.querySelector(".container2 p#output")
|
||||
const info = document.querySelector("#info")
|
||||
const link = document.querySelector("#link")
|
||||
const time = document.querySelector("#time")
|
||||
const waiting = document.querySelector("#waiting")
|
||||
|
||||
document.querySelector("input#next").addEventListener("click", (e) => {
|
||||
|
@ -63,11 +68,11 @@ window.onload = function() {
|
|||
})
|
||||
|
||||
ws.onopen = (e) => {
|
||||
waiting.style.display = "none";
|
||||
waiting.classList.add("u-hidden")
|
||||
}
|
||||
|
||||
ws.onclose = (e) => {
|
||||
waiting.style.display = "block";
|
||||
waiting.classList.remove("u-hidden")
|
||||
}
|
||||
|
||||
ws.onmessage = (e) => {
|
||||
|
@ -88,14 +93,14 @@ window.onload = function() {
|
|||
el.style.removeProperty("background-color")
|
||||
})
|
||||
if (data.payload.pause) {
|
||||
info.style.display = "none"
|
||||
info.classList.add("u-hidden")
|
||||
} else {
|
||||
info.style.display = "block"
|
||||
info.children.link.children.channel.innerText = data.payload.channel
|
||||
info.children.link.children.title.innerText = data.payload.current
|
||||
info.children.link.href = "https://youtu.be/" + data.payload.song
|
||||
info.children.link.target = "_blank"
|
||||
info.children.time.innerText = `( ${msToTime(data.payload.position)} / ${msToTime(data.payload.len)} )`
|
||||
info.classList.remove("u-hidden")
|
||||
link.children.channel.innerText = data.payload.channel
|
||||
link.children.title.innerText = data.payload.current
|
||||
link.href = "https://youtu.be/" + data.payload.song
|
||||
link.target = "_blank"
|
||||
time.innerText = `${msToTime(data.payload.position)} / ${msToTime(data.payload.len)}`
|
||||
document.querySelector(`#items > div[data-id='${data.payload.playlist}']`).style.backgroundColor = "burlywood"
|
||||
}
|
||||
setTimeout(() => {
|
||||
|
@ -103,15 +108,15 @@ window.onload = function() {
|
|||
}, 1000);
|
||||
break
|
||||
case "song_position":
|
||||
info.children.time.innerText = `( ${msToTime(data.payload.position)} / ${msToTime(data.payload.len)} )`
|
||||
info.style.display = "block"
|
||||
time.innerText = `${msToTime(data.payload.position)} / ${msToTime(data.payload.len)}`
|
||||
info.classList.remove("u-hidden")
|
||||
break
|
||||
case "stop":
|
||||
setTimeout(() => {
|
||||
items.style.pointerEvents = 'auto'
|
||||
}, 2000);
|
||||
|
||||
info.style.display = "none"
|
||||
info.classList.add("u-hidden")
|
||||
break
|
||||
case "new_playlist":
|
||||
addPlaylist(data.payload);
|
||||
|
@ -163,7 +168,7 @@ window.onload = function() {
|
|||
url.innerText = ""
|
||||
})
|
||||
|
||||
document.querySelectorAll("#items").forEach(item => item.addEventListener("click", (e) => {
|
||||
document.querySelectorAll("#items .item").forEach(item => item.addEventListener("click", (e) => {
|
||||
e.preventDefault()
|
||||
e.target.parentElement.style.pointerEvents = 'none'
|
||||
const disableui = setTimeout((t) => {
|
||||
|
@ -177,7 +182,7 @@ window.onload = function() {
|
|||
}))
|
||||
}));
|
||||
|
||||
document.querySelectorAll("#ambiance").forEach(item => item.addEventListener("click", (e) => {
|
||||
document.querySelectorAll("#ambiance .item").forEach(item => item.addEventListener("click", (e) => {
|
||||
e.preventDefault()
|
||||
e.target.parentElement.style.pointerEvents = 'none'
|
||||
const disableui = setTimeout((t) => {
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"scripts": {
|
||||
"build": "esbuild js/script.js --bundle --outdir=public/"
|
||||
},
|
||||
"dependencies": {
|
||||
"esbuild": "^0.15.14",
|
||||
"reconnecting-websocket": "^4.4.0",
|
||||
"sortablejs": "^1.15.0"
|
||||
}
|
||||
}
|
135
queue.go
135
queue.go
|
@ -1,11 +1,14 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/faiface/beep"
|
||||
"github.com/kataras/go-events"
|
||||
|
||||
"dndmusicbot/ffmpeg"
|
||||
discordspeaker "dndmusicbot/speaker"
|
||||
)
|
||||
|
||||
|
@ -15,68 +18,130 @@ type Ambiance struct {
|
|||
}
|
||||
|
||||
func init() {
|
||||
app.ambiance = new(Queue)
|
||||
app.ambiance.Events = app.events
|
||||
discordspeaker.Play(app.ambiance)
|
||||
app.ambiance = beep.Mixer{}
|
||||
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
|
||||
Events events.EventEmmiter
|
||||
playing bool
|
||||
yt VideoInfo
|
||||
list *list.List
|
||||
current *list.Element
|
||||
}
|
||||
|
||||
func (q Queue) IsPlaying() bool {
|
||||
return q.playing
|
||||
}
|
||||
|
||||
func (q *Queue) Add(streamers ...beep.StreamSeekCloser) {
|
||||
func (q *Queue) Play() {
|
||||
q.playing = true
|
||||
q.streamers = append(q.streamers, streamers...)
|
||||
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 len(q.streamers)
|
||||
return q.list.Len()
|
||||
}
|
||||
|
||||
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
|
||||
func (q Queue) Current() *Song {
|
||||
if q.current == nil {
|
||||
return new(Song)
|
||||
}
|
||||
return q.current.Value.(*Song)
|
||||
}
|
||||
|
||||
func (q *Queue) Reset() {
|
||||
q.playing = false
|
||||
for _, stream := range q.streamers {
|
||||
stream.Close()
|
||||
|
||||
q.current = nil
|
||||
q.list = q.list.Init()
|
||||
}
|
||||
|
||||
func (q *Queue) Next() {
|
||||
if q.current == nil {
|
||||
return
|
||||
}
|
||||
q.streamers = nil
|
||||
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() {
|
||||
}
|
||||
|
||||
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 len(q.streamers) == 0 {
|
||||
if q.current == nil || q.list.Len() == 0 {
|
||||
for i := range samples[filled:] {
|
||||
samples[i][0] = 0
|
||||
samples[i][1] = 0
|
||||
|
@ -85,11 +150,11 @@ func (q *Queue) Stream(samples [][2]float64) (n int, ok bool) {
|
|||
}
|
||||
|
||||
// We stream from the first streamer in the queue.
|
||||
n, ok := q.streamers[0].Stream(samples[filled:])
|
||||
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.streamers = q.streamers[1:]
|
||||
q.Next()
|
||||
q.Events.Emit("song_over", nil)
|
||||
}
|
||||
// We update the number of filled samples.
|
||||
|
@ -103,9 +168,17 @@ func (q *Queue) Err() error {
|
|||
}
|
||||
|
||||
func (q *Queue) Position() int {
|
||||
if len(q.streamers) > 0 {
|
||||
return q.streamers[0].Position()
|
||||
} else {
|
||||
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())
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ func init() {
|
|||
app.router.GET("/play/:playlist", app.Play)
|
||||
app.router.GET("/reset", app.Reset)
|
||||
|
||||
app.router.ServeFiles("/js/*filepath", http.Dir("js"))
|
||||
app.router.ServeFiles("/js/*filepath", http.Dir("public"))
|
||||
app.router.ServeFiles("/css/*filepath", http.Dir("css"))
|
||||
|
||||
app.router.HandlerFunc("GET", "/ws", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -5,47 +5,67 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>D&D Music Bot!</title>
|
||||
<link rel="stylesheet" href="/css/solarized-dark.min.css">
|
||||
<link rel="stylesheet" href="/css/solarized-dark.css">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div><h2 class="bot">Playlists</h2></div>
|
||||
<div id="items" class="container">
|
||||
{{ range .Playlists }}
|
||||
<div class="item" data-id="{{ .Id }}">{{ .Title }}</div>
|
||||
{{ end}}
|
||||
<div class="item locked" data-id="reset">Stop</div>
|
||||
</div>
|
||||
<div id="inputplaylist" class="container2">
|
||||
<input name="title" type="text" placeholder="Enter name..">
|
||||
<input name="url" type="text" placeholder="https://youtube.com/playlist?list=...">
|
||||
<input id="addplaylist" name="submit" value="Add" type="submit">
|
||||
</div>
|
||||
<div class="container2">
|
||||
<h2 class="bot">Playlists</h2>
|
||||
|
||||
<section>
|
||||
<div id="items" class="container">
|
||||
{{ range .Playlists }}
|
||||
<div class="item" data-id="{{ .Id }}">{{ .Title }}</div>
|
||||
{{ end}}
|
||||
<div class="item locked stop" data-id="reset">Stop</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div id="inputplaylist" class="input-container">
|
||||
<input class="u-full-width" name="title" type="text" placeholder="Enter name..">
|
||||
<input class="u-full-width" name="url" type="text" placeholder="https://youtube.com/playlist?list=...">
|
||||
<input id="addplaylist" name="submit" value="Add" type="submit">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<p id="output"></p>
|
||||
</div>
|
||||
<div id="info" class="container2">
|
||||
<input id="prev" name="prev" type="button" value="prev">
|
||||
<input id="next" name="next" type="button" value="next">
|
||||
<a id="link" style="text-decoration: none;">
|
||||
<span id="channel"></span>
|
||||
<span> - </span>
|
||||
<span id="title"></span>
|
||||
</a>
|
||||
<span id="time"></span>
|
||||
</div>
|
||||
<div><h2 class="bot">Ambiance</h2></div>
|
||||
<div id="ambiance" class="container">
|
||||
{{ range .Ambiance }}
|
||||
<div class="item drag" data-id="{{ . }}">{{ . }}</div>
|
||||
{{ end}}
|
||||
<div class="item locked" data-id="reset">Stop</div>
|
||||
</div>
|
||||
<div id="inputambiance" class="container2">
|
||||
<input name="title" type="text" placeholder="Enter name..">
|
||||
<input name="url" type="text" placeholder="Enter url...">
|
||||
<input id="addambiance" name="submit" value="Add" type="submit">
|
||||
</div>
|
||||
|
||||
<div id="info">
|
||||
<a id="link" style="text-decoration: none;">
|
||||
<span id="channel"></span>
|
||||
<span> - </span>
|
||||
<span id="title"></span>
|
||||
</a>
|
||||
|
||||
<div class="controls">
|
||||
<input id="prev" name="prev" type="button" value="prev">
|
||||
<span id="time"></span>
|
||||
<input id="next" name="next" type="button" value="next">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<h2 class="bot">Ambiance</h2>
|
||||
|
||||
<section>
|
||||
<div id="ambiance" class="container">
|
||||
{{ range .Ambiance }}
|
||||
<div class="item drag" data-id="{{ . }}">{{ . }}</div>
|
||||
{{ end}}
|
||||
<div class="item locked stop" data-id="reset">Stop</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div id="inputambiance" class="input-container">
|
||||
<input class="u-full-width" name="title" type="text" placeholder="Enter name..">
|
||||
<input class="u-full-width" name="url" type="text" placeholder="Enter url...">
|
||||
<input id="addambiance" name="submit" value="Add" type="submit">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- The Modal -->
|
||||
<div id="waiting" class="modal">
|
||||
<!-- Modal content -->
|
||||
|
@ -53,8 +73,6 @@
|
|||
<p>Waiting for bot..</p>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/reconnecting-websocket.min.js"></script>
|
||||
<script src="/js/Sortable.min.js"></script>
|
||||
<script src="/js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
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.IsPlaying() {
|
||||
if app.ambiance.Len() > 0 {
|
||||
msg := make(map[string]interface{})
|
||||
out := make(map[string]interface{})
|
||||
msg["event"] = "ambiance_play"
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@esbuild/android-arm@0.15.14":
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.14.tgz#5d0027f920eeeac313c01fd6ecb8af50c306a466"
|
||||
integrity sha512-+Rb20XXxRGisNu2WmNKk+scpanb7nL5yhuI1KR9wQFiC43ddPj/V1fmNyzlFC9bKiG4mYzxW7egtoHVcynr+OA==
|
||||
|
||||
"@esbuild/linux-loong64@0.15.14":
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.14.tgz#1221684955c44385f8af34f7240088b7dc08d19d"
|
||||
integrity sha512-eQi9rosGNVQFJyJWV0HCA5WZae/qWIQME7s8/j8DMvnylfBv62Pbu+zJ2eUDqNf2O4u3WB+OEXyfkpBoe194sg==
|
||||
|
||||
esbuild-android-64@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.14.tgz#114e55b0d58fb7b45d7fa3d93516bd13fc8869cc"
|
||||
integrity sha512-HuilVIb4rk9abT4U6bcFdU35UHOzcWVGLSjEmC58OVr96q5UiRqzDtWjPlCMugjhgUGKEs8Zf4ueIvYbOStbIg==
|
||||
|
||||
esbuild-android-arm64@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.14.tgz#8541f38a9aacf88e574fb13f5ad4ca51a04c12bb"
|
||||
integrity sha512-/QnxRVxsR2Vtf3XottAHj7hENAMW2wCs6S+OZcAbc/8nlhbAL/bCQRCVD78VtI5mdwqWkVi3wMqM94kScQCgqg==
|
||||
|
||||
esbuild-darwin-64@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.14.tgz#b40b334db81ff1e3677a6712b23761748a157c57"
|
||||
integrity sha512-ToNuf1uifu8hhwWvoZJGCdLIX/1zpo8cOGnT0XAhDQXiKOKYaotVNx7pOVB1f+wHoWwTLInrOmh3EmA7Fd+8Vg==
|
||||
|
||||
esbuild-darwin-arm64@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.14.tgz#44b5c1477bb7bdb852dd905e906f68765e2828bc"
|
||||
integrity sha512-KgGP+y77GszfYJgceO0Wi/PiRtYo5y2Xo9rhBUpxTPaBgWDJ14gqYN0+NMbu+qC2fykxXaipHxN4Scaj9tUS1A==
|
||||
|
||||
esbuild-freebsd-64@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.14.tgz#8c57315d238690f34b6ed0c94e5cfc04c858247a"
|
||||
integrity sha512-xr0E2n5lyWw3uFSwwUXHc0EcaBDtsal/iIfLioflHdhAe10KSctV978Te7YsfnsMKzcoGeS366+tqbCXdqDHQA==
|
||||
|
||||
esbuild-freebsd-arm64@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.14.tgz#2e92acca09258daa849e635565f52469266f0b7b"
|
||||
integrity sha512-8XH96sOQ4b1LhMlO10eEWOjEngmZ2oyw3pW4o8kvBcpF6pULr56eeYVP5radtgw54g3T8nKHDHYEI5AItvskZg==
|
||||
|
||||
esbuild-linux-32@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.14.tgz#ca5ed3e9dff82df486ddde362d7e00775a597dfd"
|
||||
integrity sha512-6ssnvwaTAi8AzKN8By2V0nS+WF5jTP7SfuK6sStGnDP7MCJo/4zHgM9oE1eQTS2jPmo3D673rckuCzRlig+HMA==
|
||||
|
||||
esbuild-linux-64@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.14.tgz#42952e1d08a299d5f573c567639fb37b033befbf"
|
||||
integrity sha512-ONySx3U0wAJOJuxGUlXBWxVKFVpWv88JEv0NZ6NlHknmDd1yCbf4AEdClSgLrqKQDXYywmw4gYDvdLsS6z0hcw==
|
||||
|
||||
esbuild-linux-arm64@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.14.tgz#0c0d788099703327ec0ae70758cb2639ef6c5d88"
|
||||
integrity sha512-kle2Ov6a1e5AjlHlMQl1e+c4myGTeggrRzArQFmWp6O6JoqqB9hT+B28EW4tjFWgV/NxUq46pWYpgaWXsXRPAg==
|
||||
|
||||
esbuild-linux-arm@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.14.tgz#751a5ca5042cd60f669b07c3bcec3dd6c4f8151c"
|
||||
integrity sha512-D2LImAIV3QzL7lHURyCHBkycVFbKwkDb1XEUWan+2fb4qfW7qAeUtul7ZIcIwFKZgPcl+6gKZmvLgPSj26RQ2Q==
|
||||
|
||||
esbuild-linux-mips64le@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.14.tgz#da8ac35f2704de0b52bf53a99c12f604fbe9b916"
|
||||
integrity sha512-FVdMYIzOLXUq+OE7XYKesuEAqZhmAIV6qOoYahvUp93oXy0MOVTP370ECbPfGXXUdlvc0TNgkJa3YhEwyZ6MRA==
|
||||
|
||||
esbuild-linux-ppc64le@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.14.tgz#a315b5016917429080c3d32e03319f1ff876ac55"
|
||||
integrity sha512-2NzH+iuzMDA+jjtPjuIz/OhRDf8tzbQ1tRZJI//aT25o1HKc0reMMXxKIYq/8nSHXiJSnYV4ODzTiv45s+h73w==
|
||||
|
||||
esbuild-linux-riscv64@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.14.tgz#9f2e0a935e5086d398fc19c7ff5d217bfefe3e12"
|
||||
integrity sha512-VqxvutZNlQxmUNS7Ac+aczttLEoHBJ9e3OYGqnULrfipRvG97qLrAv9EUY9iSrRKBqeEbSvS9bSfstZqwz0T4Q==
|
||||
|
||||
esbuild-linux-s390x@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.14.tgz#53108112faff5a4e1bad17f7b0b0ffa1df4b7efb"
|
||||
integrity sha512-+KVHEUshX5n6VP6Vp/AKv9fZIl5kr2ph8EUFmQUJnDpHwcfTSn2AQgYYm0HTBR2Mr4d0Wlr0FxF/Cs5pbFgiOw==
|
||||
|
||||
esbuild-netbsd-64@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.14.tgz#5330efc41fe4f1c2bab5462bcfe7a4ffce7ba00a"
|
||||
integrity sha512-6D/dr17piEgevIm1xJfZP2SjB9Z+g8ERhNnBdlZPBWZl+KSPUKLGF13AbvC+nzGh8IxOH2TyTIdRMvKMP0nEzQ==
|
||||
|
||||
esbuild-openbsd-64@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.14.tgz#ee64944d863e937611fc31adf349e9bb4f5f7eac"
|
||||
integrity sha512-rREQBIlMibBetgr2E9Lywt2Qxv2ZdpmYahR4IUlAQ1Efv/A5gYdO0/VIN3iowDbCNTLxp0bb57Vf0LFcffD6kA==
|
||||
|
||||
esbuild-sunos-64@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.14.tgz#29b0b20de6fe6ef50f9fbe533ec20dc4b595f9aa"
|
||||
integrity sha512-DNVjSp/BY4IfwtdUAvWGIDaIjJXY5KI4uD82+15v6k/w7px9dnaDaJJ2R6Mu+KCgr5oklmFc0KjBjh311Gxl9Q==
|
||||
|
||||
esbuild-windows-32@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.14.tgz#05e9b159d664809f7a4a8a68ed048d193457b27d"
|
||||
integrity sha512-pHBWrcA+/oLgvViuG9FO3kNPO635gkoVrRQwe6ZY1S0jdET07xe2toUvQoJQ8KT3/OkxqUasIty5hpuKFLD+eg==
|
||||
|
||||
esbuild-windows-64@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.14.tgz#d5ae086728ab30b72969e40ed0a7a0d9082f2cdd"
|
||||
integrity sha512-CszIGQVk/P8FOS5UgAH4hKc9zOaFo69fe+k1rqgBHx3CSK3Opyk5lwYriIamaWOVjBt7IwEP6NALz+tkVWdFog==
|
||||
|
||||
esbuild-windows-arm64@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.14.tgz#8eb50ab9a0ecaf058593fbad17502749306f801d"
|
||||
integrity sha512-KW9W4psdZceaS9A7Jsgl4WialOznSURvqX/oHZk3gOP7KbjtHLSsnmSvNdzagGJfxbAe30UVGXRe8q8nDsOSQw==
|
||||
|
||||
esbuild@^0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.14.tgz#09202b811f1710363d5088a3401a351351c79875"
|
||||
integrity sha512-pJN8j42fvWLFWwSMG4luuupl2Me7mxciUOsMegKvwCmhEbJ2covUdFnihxm0FMIBV+cbwbtMoHgMCCI+pj1btQ==
|
||||
optionalDependencies:
|
||||
"@esbuild/android-arm" "0.15.14"
|
||||
"@esbuild/linux-loong64" "0.15.14"
|
||||
esbuild-android-64 "0.15.14"
|
||||
esbuild-android-arm64 "0.15.14"
|
||||
esbuild-darwin-64 "0.15.14"
|
||||
esbuild-darwin-arm64 "0.15.14"
|
||||
esbuild-freebsd-64 "0.15.14"
|
||||
esbuild-freebsd-arm64 "0.15.14"
|
||||
esbuild-linux-32 "0.15.14"
|
||||
esbuild-linux-64 "0.15.14"
|
||||
esbuild-linux-arm "0.15.14"
|
||||
esbuild-linux-arm64 "0.15.14"
|
||||
esbuild-linux-mips64le "0.15.14"
|
||||
esbuild-linux-ppc64le "0.15.14"
|
||||
esbuild-linux-riscv64 "0.15.14"
|
||||
esbuild-linux-s390x "0.15.14"
|
||||
esbuild-netbsd-64 "0.15.14"
|
||||
esbuild-openbsd-64 "0.15.14"
|
||||
esbuild-sunos-64 "0.15.14"
|
||||
esbuild-windows-32 "0.15.14"
|
||||
esbuild-windows-64 "0.15.14"
|
||||
esbuild-windows-arm64 "0.15.14"
|
||||
|
||||
reconnecting-websocket@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz#3b0e5b96ef119e78a03135865b8bb0af1b948783"
|
||||
integrity sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==
|
||||
|
||||
sortablejs@^1.15.0:
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.15.0.tgz#53230b8aa3502bb77a29e2005808ffdb4a5f7e2a"
|
||||
integrity sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w==
|
62
youtube.go
62
youtube.go
|
@ -1,10 +1,12 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"math/big"
|
||||
|
@ -13,6 +15,7 @@ import (
|
|||
mrand "math/rand"
|
||||
|
||||
"github.com/faiface/beep"
|
||||
"github.com/sosodev/duration"
|
||||
"google.golang.org/api/option"
|
||||
"google.golang.org/api/youtube/v3"
|
||||
)
|
||||
|
@ -57,7 +60,7 @@ func ShufflePlaylist(list []string) ([]string, error) {
|
|||
}
|
||||
|
||||
func (app *App) GetSong(list []string) string {
|
||||
return fmt.Sprintf(yt_url, list[app.plidx])
|
||||
return list[app.plidx]
|
||||
}
|
||||
|
||||
func (app *App) GetNextSong(list []string) string {
|
||||
|
@ -65,7 +68,7 @@ func (app *App) GetNextSong(list []string) string {
|
|||
if app.plidx >= len(app.active) {
|
||||
app.plidx = 0
|
||||
}
|
||||
return fmt.Sprintf(yt_url, list[app.plidx])
|
||||
return list[app.plidx]
|
||||
}
|
||||
|
||||
func (app *App) GetPrevSong(list []string) string {
|
||||
|
@ -73,7 +76,7 @@ func (app *App) GetPrevSong(list []string) string {
|
|||
if app.plidx < 0 {
|
||||
app.plidx = len(list)
|
||||
}
|
||||
return fmt.Sprintf(yt_url, list[app.plidx])
|
||||
return list[app.plidx]
|
||||
}
|
||||
|
||||
func GetRandomSong(list []string) string {
|
||||
|
@ -86,7 +89,56 @@ func GetRandomSong(list []string) string {
|
|||
log.Println("Failed to get random int, ", err)
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(yt_url, list[idx.Int64()])
|
||||
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
|
||||
}
|
||||
|
||||
func (app App) Playlist(playlist string) ([]string, error) {
|
||||
|
|
69
ytdl/ytdl.go
69
ytdl/ytdl.go
|
@ -1,72 +1,31 @@
|
|||
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 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",
|
||||
"--no-call-home",
|
||||
"--no-cache-dir",
|
||||
"--ignore-errors",
|
||||
"--newline",
|
||||
"--restrict-filenames",
|
||||
"-f", "140",
|
||||
"-j",
|
||||
).Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("%s is now cached.\n", vid)
|
||||
|
||||
return js, nil
|
||||
})
|
||||
|
||||
func NewYTdl(vid string) ([]byte, error) {
|
||||
uri, err := exec.Command(
|
||||
"./bin/yt-dlp_linux",
|
||||
fmt.Sprintf(yturl, vid),
|
||||
"--cookies", "./cookies.txt",
|
||||
"--no-call-home",
|
||||
"--no-cache-dir",
|
||||
"--ignore-errors",
|
||||
"--newline",
|
||||
"--restrict-filenames",
|
||||
"-f", "140",
|
||||
"--get-url",
|
||||
).Output()
|
||||
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
|
||||
return uri, nil
|
||||
}
|
||||
|
||||
func DownloadAmbiance(uri string, name string) error {
|
||||
|
|
Loading…
Reference in New Issue