Compare commits
No commits in common. "master" and "haste/handle-touch" have entirely different histories.
master
...
haste/hand
|
@ -1,16 +0,0 @@
|
||||||
ambiance
|
|
||||||
bin
|
|
||||||
cache
|
|
||||||
config
|
|
||||||
cookies.txt
|
|
||||||
dndmusicbot
|
|
||||||
Dockerfile
|
|
||||||
.git
|
|
||||||
.gitignore
|
|
||||||
.dockerignore
|
|
||||||
.jsimage
|
|
||||||
k8s
|
|
||||||
MPD
|
|
||||||
oauth.cfg
|
|
||||||
test
|
|
||||||
src
|
|
|
@ -10,12 +10,8 @@ js/*.min.js
|
||||||
cache
|
cache
|
||||||
|
|
||||||
bin
|
bin
|
||||||
ambiance
|
ambiance/*.mp3
|
||||||
|
|
||||||
*.yml
|
*.yml
|
||||||
public/
|
public/
|
||||||
node_modules/
|
node_modules/
|
||||||
src/node_modules
|
|
||||||
*.aac
|
|
||||||
*.opus
|
|
||||||
*.mp3
|
|
|
@ -2,8 +2,10 @@
|
||||||
# docker run -it -v $(pwd)/public:/app/public dndmusicbot-js-build
|
# docker run -it -v $(pwd)/public:/app/public dndmusicbot-js-build
|
||||||
FROM node:19-alpine3.16
|
FROM node:19-alpine3.16
|
||||||
|
|
||||||
RUN mkdir /public && chmod 777 /public
|
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
ENTRYPOINT ["yarn"]
|
COPY . /app
|
||||||
|
|
||||||
|
RUN yarn
|
||||||
|
|
||||||
|
ENTRYPOINT ["yarn", "build"]
|
|
@ -1,61 +0,0 @@
|
||||||
FROM golang:1.21-bookworm as builder
|
|
||||||
|
|
||||||
ENV FFMPEG_VERSION=5.1.2
|
|
||||||
ENV MPD_VERSION=0.23.14
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get -y install \
|
|
||||||
libopus-dev libopusfile-dev \
|
|
||||||
meson g++ nasm \
|
|
||||||
libfmt-dev \
|
|
||||||
libpcre2-dev \
|
|
||||||
libmad0-dev libmpg123-dev libid3tag0-dev \
|
|
||||||
libflac-dev libvorbis-dev libopus-dev libogg-dev \
|
|
||||||
libadplug-dev libaudiofile-dev libsndfile1-dev libfaad-dev \
|
|
||||||
libsamplerate0-dev libsoxr-dev \
|
|
||||||
libcurl4-gnutls-dev \
|
|
||||||
libboost-dev \
|
|
||||||
zlib1g-dev
|
|
||||||
WORKDIR /tmp
|
|
||||||
RUN curl -LOs http://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.xz && tar xvf ffmpeg-${FFMPEG_VERSION}.tar.xz && \
|
|
||||||
cd ffmpeg-${FFMPEG_VERSION} && \
|
|
||||||
./configure \
|
|
||||||
--enable-version3 \
|
|
||||||
--enable-gpl \
|
|
||||||
--enable-nonfree \
|
|
||||||
--enable-small \
|
|
||||||
--enable-libvorbis \
|
|
||||||
--enable-libopus \
|
|
||||||
--enable-postproc \
|
|
||||||
--enable-openssl \
|
|
||||||
--disable-debug && \
|
|
||||||
make && \
|
|
||||||
make install
|
|
||||||
RUN curl -LOs https://www.musicpd.org/download/mpd/0.23/mpd-${MPD_VERSION}.tar.xz && tar xvf mpd-${MPD_VERSION}.tar.xz && \
|
|
||||||
cd mpd-${MPD_VERSION} && \
|
|
||||||
meson . output/release --buildtype=minsize -Db_ndebug=true && \
|
|
||||||
ninja -C output/release
|
|
||||||
WORKDIR /src
|
|
||||||
COPY . .
|
|
||||||
RUN go build
|
|
||||||
FROM debian:bookworm-slim
|
|
||||||
RUN apt-get update && apt-get -y install \
|
|
||||||
ca-certificates \
|
|
||||||
libopus-dev libopusfile-dev \
|
|
||||||
libfmt-dev \
|
|
||||||
libpcre2-dev \
|
|
||||||
libmad0-dev libmpg123-dev libid3tag0-dev \
|
|
||||||
libflac-dev libvorbis-dev libopus-dev libogg-dev \
|
|
||||||
libadplug-dev libaudiofile-dev libsndfile1-dev libfaad-dev \
|
|
||||||
libsamplerate0-dev libsoxr-dev \
|
|
||||||
libcurl4-gnutls-dev \
|
|
||||||
curl && \
|
|
||||||
curl -L https://github.com/badaix/snapcast/releases/download/v0.27.0/snapclient_0.27.0-1_without-pulse_amd64.deb -o /tmp/snapcast.deb && \
|
|
||||||
curl -L http://ftp.no.debian.org/debian/pool/main/f/flac/libflac8_1.3.3-2+deb11u2_amd64.deb -o /tmp/libflac8.deb && \
|
|
||||||
apt -y install /tmp/snapcast.deb /tmp/libflac8.deb && rm -rf /tmp/*.deb
|
|
||||||
COPY --from=builder /src/dndmusicbot /app/
|
|
||||||
COPY --from=builder /tmp/mpd-0.23.14/output/release/mpd /usr/local/bin
|
|
||||||
COPY --from=builder /usr/local/bin/ffmpeg /usr/local/bin
|
|
||||||
COPY mp3 /app/mp3
|
|
||||||
ADD tmpl /app/tmpl
|
|
||||||
WORKDIR /app
|
|
||||||
ENTRYPOINT [ "/app/dndmusicbot" ]
|
|
|
@ -1,16 +0,0 @@
|
||||||
ambiance
|
|
||||||
bin
|
|
||||||
cache
|
|
||||||
config
|
|
||||||
cookies.txt
|
|
||||||
dndmusicbot
|
|
||||||
Dockerfile
|
|
||||||
.git
|
|
||||||
.gitignore
|
|
||||||
.dockerignore
|
|
||||||
.jsimage
|
|
||||||
k8s
|
|
||||||
MPD
|
|
||||||
oauth.cfg
|
|
||||||
test
|
|
||||||
src
|
|
|
@ -1,2 +0,0 @@
|
||||||
ambiance
|
|
||||||
*.mp3
|
|
37
Makefile
37
Makefile
|
@ -1,37 +0,0 @@
|
||||||
.DEFAULT_GOAL := build
|
|
||||||
APPLICATION_NAME ?= dndmusicbot
|
|
||||||
CURRENT_UID := $(shell id -u)
|
|
||||||
BIN := src/node_modules/.bin
|
|
||||||
YARN = docker run --rm --user $(CURRENT_UID) -v $(CURDIR)/src:/app -v $(CURDIR)/public:/public dndmusicbot-js-build
|
|
||||||
|
|
||||||
PHONY: js image build clean
|
|
||||||
|
|
||||||
js: image public/script.js
|
|
||||||
|
|
||||||
image: .jsimage
|
|
||||||
|
|
||||||
.jsimage: export DOCKER_BUILDKIT=1
|
|
||||||
.jsimage: Dockerfile.js
|
|
||||||
@docker build -t dndmusicbot-js-build . -f Dockerfile.js
|
|
||||||
@touch .jsimage
|
|
||||||
|
|
||||||
src/node_modules:
|
|
||||||
mkdir -p $@
|
|
||||||
|
|
||||||
public:
|
|
||||||
mkdir -p $@
|
|
||||||
|
|
||||||
src/yarn.lock: public src/node_modules src/package.json
|
|
||||||
$(YARN)
|
|
||||||
@touch -mr $(shell ls -Atd $? | head -1) $@
|
|
||||||
|
|
||||||
public/script.js: src/script.js src/yarn.lock
|
|
||||||
$(YARN) build
|
|
||||||
|
|
||||||
$(APPLICATION_NAME): *.go ffmpeg/*.go speaker/*.go go.mod go.sum
|
|
||||||
@go build
|
|
||||||
|
|
||||||
build: image js $(APPLICATION_NAME)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -r public/* src/node_modules .jsimage $(APPLICATION_NAME)
|
|
219
ambiance.go
219
ambiance.go
|
@ -1,229 +1,24 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"math"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/davecheney/xattr"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//var httpClient = new(http.Client)
|
func fnNoExt(fileName string) string {
|
||||||
|
return fileName[:len(fileName)-len(filepath.Ext(fileName))]
|
||||||
type Ambiance struct {
|
|
||||||
Id string
|
|
||||||
Title string
|
|
||||||
Path string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAmbiance(id string) (amb Ambiance, err error) {
|
func GetAmbiance() ([]string, error) {
|
||||||
fp := filepath.Join(config.GetString("ambiance.path"), id+".opus")
|
files, err := os.ReadDir("./ambiance")
|
||||||
_, err = os.Stat(fp)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
title, err := xattr.Getxattr(fp, "title")
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ambiance{
|
|
||||||
Id: id,
|
|
||||||
Title: string(title),
|
|
||||||
Path: fp,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAmbiances() (amb []Ambiance, err error) {
|
|
||||||
files, err := os.ReadDir(config.GetString("ambiance.path"))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var out []string
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
title, err := xattr.Getxattr(filepath.Join(config.GetString("ambiance.path"), file.Name()), "title")
|
out = append(out, fnNoExt(file.Name()))
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
|
|
||||||
}
|
|
||||||
amb = append(amb, Ambiance{
|
|
||||||
Id: file.Name()[:len(file.Name())-len(filepath.Ext(file.Name()))],
|
|
||||||
Title: string(title),
|
|
||||||
Path: filepath.Join(config.GetString("ambiance.path"), file.Name()),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return out, nil
|
||||||
}
|
|
||||||
|
|
||||||
func AddAmbiance(uri, title string) (Ambiance, error) {
|
|
||||||
var amb Ambiance
|
|
||||||
|
|
||||||
ev := Event{"ambiance_add_start", map[string]string{
|
|
||||||
"name": title,
|
|
||||||
}}
|
|
||||||
|
|
||||||
go ws.SendEvent(ev)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
ev = Event{"ambiance_add_finish", map[string]string{
|
|
||||||
"name": title,
|
|
||||||
}}
|
|
||||||
go ws.SendEvent(ev)
|
|
||||||
}()
|
|
||||||
|
|
||||||
tmpfile, err := exec.Command("mktemp", "/tmp/dnd_XXXXXXXXXXXX.opus").Output()
|
|
||||||
if err != nil {
|
|
||||||
return amb, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpfile = tmpfile[:len(tmpfile)-1]
|
|
||||||
|
|
||||||
log.Printf("Parsing vid from %s", uri)
|
|
||||||
vid, err := YTUrl(uri)
|
|
||||||
if err != nil {
|
|
||||||
return amb, err
|
|
||||||
}
|
|
||||||
|
|
||||||
vinfo, err := app.youtube.GetVideoFromID(vid)
|
|
||||||
if err != nil {
|
|
||||||
return amb, err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Start ffmpeg for %s (%s)", vid, vinfo.VideoDetails.Title)
|
|
||||||
ff := exec.Command(
|
|
||||||
"ffmpeg",
|
|
||||||
"-y",
|
|
||||||
"-i", vinfo.GetHLSPlaylist("234"),
|
|
||||||
"-vn",
|
|
||||||
//"-acodec", "copy",
|
|
||||||
"-movflags", "+faststart",
|
|
||||||
"-t", "01:00:00",
|
|
||||||
"-ar", "48000",
|
|
||||||
"-v", "quiet",
|
|
||||||
// "-stats",
|
|
||||||
"-progress", "pipe:1",
|
|
||||||
// "-af", "loudnorm=I=-16:LRA=11:TP=-1.5",
|
|
||||||
string(tmpfile),
|
|
||||||
)
|
|
||||||
|
|
||||||
ffprogress, err := ff.StdoutPipe()
|
|
||||||
if err != nil {
|
|
||||||
return amb, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ff.Stderr = os.Stderr
|
|
||||||
|
|
||||||
err = ff.Start()
|
|
||||||
if err != nil {
|
|
||||||
return amb, err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Start ffmpeg to extract audio to %s", string(tmpfile))
|
|
||||||
|
|
||||||
ev = Event{"ambiance_encode_start", map[string]string{
|
|
||||||
"name": title,
|
|
||||||
}}
|
|
||||||
|
|
||||||
go ws.SendEvent(ev)
|
|
||||||
|
|
||||||
data := make(map[string]string)
|
|
||||||
data["name"] = title
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(ffprogress)
|
|
||||||
|
|
||||||
for scanner.Scan() {
|
|
||||||
p := strings.Split(scanner.Text(), "=")
|
|
||||||
if len(p) == 2 {
|
|
||||||
data[p[0]] = strings.TrimSpace(p[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
prate.Do(func() {
|
|
||||||
out_time, ok := data["out_time_ms"]
|
|
||||||
if ok {
|
|
||||||
out_time_ms, _ := strconv.Atoi(out_time)
|
|
||||||
percent := fmt.Sprintf("%.0f", math.Floor((float64(out_time_ms)/float64(time.Hour.Microseconds()))*100))
|
|
||||||
data["percent"] = percent
|
|
||||||
}
|
|
||||||
|
|
||||||
go ws.SendEvent(Event{"ambiance_encode_progress", data})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
return amb, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ff.Wait()
|
|
||||||
if err != nil {
|
|
||||||
return amb, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ev = Event{"ambiance_encode_complete", map[string]string{
|
|
||||||
"name": title,
|
|
||||||
}}
|
|
||||||
|
|
||||||
go ws.SendEvent(ev)
|
|
||||||
|
|
||||||
id := uuid.New()
|
|
||||||
fn := filepath.Join(config.GetString("ambiance.path"), fmt.Sprintf("%s.aac", id.String()))
|
|
||||||
|
|
||||||
log.Printf("Moving to %s", fn)
|
|
||||||
|
|
||||||
in, err := os.Open(string(tmpfile))
|
|
||||||
if err != nil {
|
|
||||||
return amb, err
|
|
||||||
}
|
|
||||||
|
|
||||||
of, err := os.Create(fn)
|
|
||||||
if err != nil {
|
|
||||||
return amb, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = io.Copy(of, in)
|
|
||||||
if err != nil {
|
|
||||||
return amb, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = of.Sync()
|
|
||||||
if err != nil {
|
|
||||||
return amb, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = of.Close()
|
|
||||||
if err != nil {
|
|
||||||
return amb, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = in.Close()
|
|
||||||
if err != nil {
|
|
||||||
return amb, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.Remove(string(tmpfile))
|
|
||||||
if err != nil {
|
|
||||||
return amb, err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Setting xattr")
|
|
||||||
err = xattr.Setxattr(fn, "title", []byte(title))
|
|
||||||
if err != nil {
|
|
||||||
return amb, err
|
|
||||||
}
|
|
||||||
|
|
||||||
amb.Id = id.String()
|
|
||||||
amb.Title = title
|
|
||||||
|
|
||||||
log.Println("Return info.")
|
|
||||||
return amb, nil
|
|
||||||
}
|
}
|
||||||
|
|
61
bot.go
61
bot.go
|
@ -5,23 +5,18 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"dndmusicbot/youtube"
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"github.com/faiface/beep"
|
||||||
"github.com/diamondburned/arikawa/v3/state"
|
|
||||||
"github.com/diamondburned/arikawa/v3/voice"
|
|
||||||
"github.com/gohugoio/hugo/cache/filecache"
|
"github.com/gohugoio/hugo/cache/filecache"
|
||||||
"github.com/gopxl/beep"
|
|
||||||
"github.com/jackc/pgx/v5"
|
"github.com/jackc/pgx/v5"
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
"github.com/kataras/go-events"
|
"github.com/kataras/go-events"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/steino/gompd/v2/mpd"
|
"google.golang.org/api/youtube/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -37,8 +32,6 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
log.SetFlags(log.Ltime | log.Lshortfile)
|
|
||||||
|
|
||||||
log.Println("bot.go loading..")
|
log.Println("bot.go loading..")
|
||||||
config.SetConfigName("config")
|
config.SetConfigName("config")
|
||||||
config.SetConfigType("yaml")
|
config.SetConfigType("yaml")
|
||||||
|
@ -47,64 +40,44 @@ func init() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
app.plmutex = &sync.Mutex{}
|
|
||||||
|
|
||||||
log.Println("bot.go done.")
|
log.Println("bot.go done.")
|
||||||
}
|
}
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
discord *state.State
|
discord *discordgo.Session
|
||||||
voice *voice.Session
|
voice *discordgo.VoiceConnection
|
||||||
youtube *youtube.Client
|
youtube *youtube.Service
|
||||||
|
queue *Queue
|
||||||
ambiance beep.Mixer
|
ambiance beep.Mixer
|
||||||
curamb Ambiance
|
curamb string
|
||||||
events events.EventEmmiter
|
events events.EventEmmiter
|
||||||
|
next bool
|
||||||
db *pgx.Conn
|
db *pgx.Conn
|
||||||
router *httprouter.Router
|
router *httprouter.Router
|
||||||
|
active []string
|
||||||
|
plidx int
|
||||||
cache *filecache.Cache
|
cache *filecache.Cache
|
||||||
mpdc context.CancelFunc
|
|
||||||
mpdw *mpd.Watcher
|
|
||||||
mpd *mpd.Client
|
|
||||||
plmutex *sync.Mutex
|
|
||||||
plcancel context.CancelFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
var cwd string
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
ex, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
cwd = filepath.Dir(ex)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
bfs := afero.NewBasePathFs(afero.NewOsFs(), "cache")
|
bfs := afero.NewBasePathFs(afero.NewOsFs(), "cache")
|
||||||
app.cache = filecache.NewCache(bfs, 1*time.Hour, "")
|
app.cache = filecache.NewCache(bfs, 1*time.Hour, "")
|
||||||
|
|
||||||
prune := time.NewTicker(15 * time.Minute)
|
|
||||||
ticker := time.NewTicker(300 * time.Millisecond)
|
ticker := time.NewTicker(300 * time.Millisecond)
|
||||||
|
|
||||||
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
|
sc := make(chan os.Signal, 1)
|
||||||
defer cancel()
|
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-sc:
|
||||||
app.db.Close(ctx)
|
app.db.Close(context.Background())
|
||||||
app.mpdw.Close()
|
app.queue.Reset()
|
||||||
app.mpdc()
|
app.voice.Close()
|
||||||
app.voice.Leave(ctx)
|
|
||||||
app.cache.Prune(false)
|
|
||||||
dgvc()
|
|
||||||
app.discord.Close()
|
app.discord.Close()
|
||||||
return
|
return
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
app.events.Emit("tick")
|
app.events.Emit("tick")
|
||||||
case <-prune.C:
|
|
||||||
app.cache.Prune(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1
db.go
1
db.go
|
@ -26,6 +26,7 @@ type Playlist struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app App) GetPlaylists() (playlists []Playlist, err error) {
|
func (app App) GetPlaylists() (playlists []Playlist, err error) {
|
||||||
|
log.Println(app.db.Ping(context.Background()))
|
||||||
rows, err := app.db.Query(context.Background(), "SELECT id, url, title FROM playlists")
|
rows, err := app.db.Query(context.Background(), "SELECT id, url, title FROM playlists")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
45
discord.go
45
discord.go
|
@ -1,56 +1,39 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
discordspeaker "dndmusicbot/speaker"
|
discordspeaker "dndmusicbot/speaker"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/diamondburned/arikawa/v3/discord"
|
"github.com/bwmarrin/discordgo"
|
||||||
"github.com/diamondburned/arikawa/v3/state"
|
|
||||||
"github.com/diamondburned/arikawa/v3/voice"
|
|
||||||
"github.com/diamondburned/arikawa/v3/voice/voicegateway"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var dgvc context.CancelFunc
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
log.Println("discord.go loading..")
|
log.Println("discord.go loading..")
|
||||||
|
var err error
|
||||||
|
|
||||||
token := config.GetString("discord.token")
|
token := config.GetString("discord.token")
|
||||||
|
guild := config.GetString("discord.guild")
|
||||||
channel := config.GetString("discord.channel")
|
channel := config.GetString("discord.channel")
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
app.discord, err = discordgo.New("Bot " + token)
|
||||||
|
|
||||||
dgvc = cancel
|
|
||||||
s := state.New("Bot " + token)
|
|
||||||
|
|
||||||
// This is required for bots.
|
|
||||||
voice.AddIntents(s)
|
|
||||||
|
|
||||||
if err := s.Open(ctx); err != nil {
|
|
||||||
log.Fatalln("failed to open gateway:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
v, err := voice.NewSession(s)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("failed to create voice session:", err)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
chsf, err := discord.ParseSnowflake(channel)
|
// app.discord.LogLevel = discordgo.LogDebug
|
||||||
|
|
||||||
|
err = app.discord.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("failed to create snowflake:", err)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := v.JoinChannelAndSpeak(ctx, discord.ChannelID(chsf), false, true); err != nil {
|
app.voice, err = app.discord.ChannelVoiceJoin(guild, channel, false, true)
|
||||||
log.Fatalln("failed to join voice channel:", err)
|
if err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v.Speaking(ctx, voicegateway.NotSpeaking)
|
// app.voice.LogLevel = discordgo.LogDebug
|
||||||
|
|
||||||
app.discord = s
|
|
||||||
app.voice = v
|
|
||||||
|
|
||||||
discordspeaker.Init(v, config.GetInt("discord.bitrate"))
|
|
||||||
|
|
||||||
|
discordspeaker.Init(app.voice)
|
||||||
log.Println("discord.go done.")
|
log.Println("discord.go done.")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,272 @@
|
||||||
|
package discord
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/kataras/go-events"
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
GATEWAYBOTURL = "https://discord.com/api/gateway/bot"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PermissionVoiceConnect = 0x0000000000100000
|
||||||
|
PermissionVoiceSpeak = 0x0000000000200000
|
||||||
|
PermissionSendMessages = 0x0000000000000800
|
||||||
|
)
|
||||||
|
|
||||||
|
type Connection struct {
|
||||||
|
events events.EventEmmiter
|
||||||
|
httpClient *http.Client
|
||||||
|
dialer *websocket.Dialer
|
||||||
|
conn *websocket.Conn
|
||||||
|
token string
|
||||||
|
seq int
|
||||||
|
ready chan bool
|
||||||
|
vstateUpdate chan bool
|
||||||
|
vserverUpdate chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type VoiceConnection struct {
|
||||||
|
d *Connection
|
||||||
|
conn *websocket.Conn
|
||||||
|
sid string
|
||||||
|
e string
|
||||||
|
}
|
||||||
|
|
||||||
|
type VoiceStateUpdate struct {
|
||||||
|
Op int `json:"op"`
|
||||||
|
Data struct {
|
||||||
|
Guild string `json:"guild_id"`
|
||||||
|
Channel string `json:"channel_id"`
|
||||||
|
Mute bool `json:"self_mute"`
|
||||||
|
Deaf bool `json:"self_dead"`
|
||||||
|
} `json:"d"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Identify struct {
|
||||||
|
Op int `json:"op"`
|
||||||
|
|
||||||
|
Data struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
Intents int `json:"intents"`
|
||||||
|
|
||||||
|
Properties struct {
|
||||||
|
OS string `json:"os"`
|
||||||
|
Browser string `json:"browser"`
|
||||||
|
Device string `json:"device"`
|
||||||
|
} `json:"properties"`
|
||||||
|
} `json:"d"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnection(token string) (*Connection, error) {
|
||||||
|
conn := new(Connection)
|
||||||
|
|
||||||
|
conn.token = token
|
||||||
|
|
||||||
|
conn.ready = make(chan bool)
|
||||||
|
conn.vserverUpdate = make(chan bool)
|
||||||
|
conn.vstateUpdate = make(chan bool)
|
||||||
|
|
||||||
|
conn.events = events.New()
|
||||||
|
conn.events.On("READY", conn.eventready)
|
||||||
|
|
||||||
|
conn.dialer = &websocket.Dialer{
|
||||||
|
HandshakeTimeout: 45 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.httpClient = &http.Client{}
|
||||||
|
gw, err := conn.GetGateway()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.conn, _, err = conn.dialer.Dial(gw, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
_, b, err := conn.conn.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
go conn.handleGatewayEvent(b)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Identify
|
||||||
|
id := new(Identify)
|
||||||
|
id.Op = 2
|
||||||
|
id.Data.Token = token
|
||||||
|
id.Data.Intents = PermissionVoiceConnect | PermissionVoiceSpeak | PermissionSendMessages
|
||||||
|
id.Data.Properties.OS = "linux"
|
||||||
|
id.Data.Properties.Browser = "dndmusicbot/0.0.1"
|
||||||
|
id.Data.Properties.Device = "dndmusicbot"
|
||||||
|
|
||||||
|
json.NewEncoder(os.Stdout).Encode(id)
|
||||||
|
|
||||||
|
err = conn.conn.WriteJSON(id)
|
||||||
|
if err != nil {
|
||||||
|
conn.conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Wait for Ready!
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, err
|
||||||
|
case <-conn.ready:
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connection) NewVoiceConnection(guild string, channel string) (*VoiceConnection, error) {
|
||||||
|
vc := new(VoiceConnection)
|
||||||
|
vc.d = c
|
||||||
|
|
||||||
|
c.events.On("VOICE_STATE_UPDATE", vc.voiceStateUpdate)
|
||||||
|
c.events.On("VOICE_SERVER_UDPATE", vc.voiceServerUpdate)
|
||||||
|
|
||||||
|
msg := new(VoiceStateUpdate)
|
||||||
|
msg.Op = 4
|
||||||
|
msg.Data.Guild = guild
|
||||||
|
msg.Data.Channel = channel
|
||||||
|
msg.Data.Mute = false
|
||||||
|
msg.Data.Deaf = true
|
||||||
|
|
||||||
|
c.conn.WriteJSON(msg)
|
||||||
|
|
||||||
|
var state, server bool
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// We have to wait for both a vserverupdate and a vstateupdate before we continue.
|
||||||
|
for !state && !server {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case <-c.vserverUpdate:
|
||||||
|
server = true
|
||||||
|
case <-c.vstateUpdate:
|
||||||
|
state = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should have gotten all the information by now.. continue..
|
||||||
|
|
||||||
|
return vc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vc *VoiceConnection) voiceStateUpdate(payload ...interface{}) {
|
||||||
|
vc.d.vstateUpdate <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vc *VoiceConnection) voiceServerUpdate(payload ...interface{}) {
|
||||||
|
vc.d.vserverUpdate <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ready event. Contains alot of information regarding our session.
|
||||||
|
func (c *Connection) eventready(payload ...interface{}) {
|
||||||
|
c.ready <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connection) handleGatewayEvent(js []byte) error {
|
||||||
|
eventdata := gjson.GetManyBytes(js, "op", "d", "s", "t")
|
||||||
|
|
||||||
|
op := eventdata[0]
|
||||||
|
data := eventdata[1]
|
||||||
|
seq := eventdata[2]
|
||||||
|
eventname := eventdata[3]
|
||||||
|
|
||||||
|
if seq.Exists() {
|
||||||
|
c.seq = int(seq.Int())
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
c.conn.SetCloseHandler(func(code int, text string) error {
|
||||||
|
cancel()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
switch op.Int() {
|
||||||
|
case 0:
|
||||||
|
fmt.Printf("Event: %s Data: %+v\n", eventname.String(), data)
|
||||||
|
c.events.Emit(events.EventName(eventname.String()), data)
|
||||||
|
case 10: // Hello
|
||||||
|
hb_interval, err := time.ParseDuration(fmt.Sprintf("%dms", data.Get("heartbeat_interval").Int()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("We Received a Hello! Starting heartbeat on %.2fs interval\n", hb_interval.Seconds())
|
||||||
|
|
||||||
|
hb_ticker := time.NewTicker(hb_interval)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
break
|
||||||
|
case <-hb_ticker.C:
|
||||||
|
msg := make(map[string]interface{})
|
||||||
|
msg["op"] = 1
|
||||||
|
if c.seq == 0 {
|
||||||
|
msg["d"] = nil
|
||||||
|
} else {
|
||||||
|
msg["d"] = c.seq
|
||||||
|
}
|
||||||
|
c.conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
|
||||||
|
log.Printf("Sending Heartbeat: %+v\n", msg)
|
||||||
|
err = c.conn.WriteJSON(msg)
|
||||||
|
if err != nil {
|
||||||
|
c.conn.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 11:
|
||||||
|
log.Println("Heartbeat ACK!")
|
||||||
|
default:
|
||||||
|
log.Printf("%d: %+v\n", op.Int(), data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connection) GetGateway() (string, error) {
|
||||||
|
req, err := http.NewRequest("GET", GATEWAYBOTURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("Authorization", "Bot "+c.token)
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gjson.GetBytes(b, "url").String(), err
|
||||||
|
}
|
508
events.go
508
events.go
|
@ -1,22 +1,20 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"dndmusicbot/ffmpeg"
|
||||||
|
discordspeaker "dndmusicbot/speaker"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
"os"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/faiface/beep"
|
||||||
|
"github.com/faiface/beep/mp3"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/kataras/go-events"
|
"github.com/kataras/go-events"
|
||||||
"github.com/steino/gompd/v2/mpd"
|
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
|
|
||||||
ytdl "github.com/kkdai/youtube/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SongInfo struct {
|
type SongInfo struct {
|
||||||
|
@ -24,27 +22,24 @@ type SongInfo struct {
|
||||||
PlaylistName string `json:"playlistname,omitempty"`
|
PlaylistName string `json:"playlistname,omitempty"`
|
||||||
Title string `json:"current,omitempty"`
|
Title string `json:"current,omitempty"`
|
||||||
Channel string `json:"channel,omitempty"`
|
Channel string `json:"channel,omitempty"`
|
||||||
Position int64 `json:"position"`
|
Position int `json:"position"`
|
||||||
Length int64 `json:"len,omitempty"`
|
Length int `json:"len,omitempty"`
|
||||||
Pause bool `json:"pause"`
|
Pause bool `json:"pause"`
|
||||||
Song string `json:"song,omitempty"`
|
Song string `json:"song,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var l = rate.Sometimes{Interval: 800 * time.Millisecond}
|
var l = rate.Sometimes{Interval: 1 * time.Second}
|
||||||
var ytdl_client = ytdl.Client{}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
log.Println("events.go loading...")
|
log.Println("events.go loading...")
|
||||||
|
|
||||||
app.events = events.New()
|
app.events = events.New()
|
||||||
|
|
||||||
app.events.On("load_playlist", app.loadPlaylist)
|
app.events.On("load_playlist", app.loadPlaylist)
|
||||||
app.events.On("add_playlist", app.addPlaylist)
|
app.events.On("add_playlist", app.addPlaylist)
|
||||||
|
|
||||||
//app.events.On("preload_song", app.preloadSong)
|
//app.events.On("preload_song", app.preloadSong)
|
||||||
//app.events.On("song_over", app.songInfo)
|
app.events.On("song_over", app.songInfo)
|
||||||
//app.events.On("song_start", app.songInfo)
|
app.events.On("song_start", app.songInfo)
|
||||||
app.events.On("player", app.songInfo)
|
|
||||||
//app.events.On("song_position", app.songPosition)
|
//app.events.On("song_position", app.songPosition)
|
||||||
|
|
||||||
app.events.On("ambiance_play", app.ambiancePlay)
|
app.events.On("ambiance_play", app.ambiancePlay)
|
||||||
|
@ -55,187 +50,27 @@ func init() {
|
||||||
app.events.On("next", app.nextSong)
|
app.events.On("next", app.nextSong)
|
||||||
app.events.On("prev", app.prevSong)
|
app.events.On("prev", app.prevSong)
|
||||||
|
|
||||||
app.events.On("vol_up", app.volup)
|
|
||||||
app.events.On("vol_down", app.voldown)
|
|
||||||
app.events.On("vol_set", app.volset)
|
|
||||||
|
|
||||||
//app.events.On("tick", app.checkQueue)
|
//app.events.On("tick", app.checkQueue)
|
||||||
app.events.On("tick", app.songPosition)
|
app.events.On("tick", app.songPosition)
|
||||||
//app.events.On("tick", app.checkTimeleft)
|
app.events.On("tick", app.checkTimeleft)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) volup(payload ...interface{}) {
|
func (app *App) songInfoEvent(event string) map[string]interface{} {
|
||||||
if !(len(payload) > 0) {
|
msg := make(map[string]interface{})
|
||||||
log.Println("volup called without a payload.")
|
msg["event"] = event
|
||||||
return
|
|
||||||
|
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,
|
||||||
|
Length: app.queue.Len(),
|
||||||
|
Pause: !app.queue.IsPlaying(),
|
||||||
|
Song: app.queue.Current().VideoID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var t string
|
return msg
|
||||||
switch data := payload[0].(type) {
|
|
||||||
case json.RawMessage:
|
|
||||||
var err error
|
|
||||||
err = json.Unmarshal(data, &t)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
log.Println("volup called with invalid payload.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch t {
|
|
||||||
case "playlist":
|
|
||||||
pl_volume.Volume = pl_volume.Volume + 0.1
|
|
||||||
case "ambiance":
|
|
||||||
amb_volume.Volume = amb_volume.Volume + 0.1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *App) voldown(payload ...interface{}) {
|
|
||||||
if !(len(payload) > 0) {
|
|
||||||
log.Println("voldown called without a payload.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var t string
|
|
||||||
switch data := payload[0].(type) {
|
|
||||||
case json.RawMessage:
|
|
||||||
var err error
|
|
||||||
err = json.Unmarshal(data, &t)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
log.Println("voldown called with invalid payload.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch t {
|
|
||||||
case "playlist":
|
|
||||||
pl_volume.Volume = pl_volume.Volume - 0.1
|
|
||||||
case "ambiance":
|
|
||||||
amb_volume.Volume = amb_volume.Volume - 0.1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *App) volset(payload ...interface{}) {
|
|
||||||
if !(len(payload) > 0) {
|
|
||||||
log.Println("volset called without a payload.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var data map[string]string
|
|
||||||
switch js := payload[0].(type) {
|
|
||||||
case json.RawMessage:
|
|
||||||
json.Unmarshal(js, &data)
|
|
||||||
default:
|
|
||||||
log.Println("volset called with invalid payload.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vol, err := strconv.ParseFloat(data["vol"], 64)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch data["type"] {
|
|
||||||
case "playlist":
|
|
||||||
pl_volume.Volume = vol
|
|
||||||
case "ambiance":
|
|
||||||
amb_volume.Volume = vol
|
|
||||||
}
|
|
||||||
|
|
||||||
ev := Event{"volume", map[string]float64{
|
|
||||||
"playlist": pl_volume.Volume,
|
|
||||||
"ambiance": amb_volume.Volume,
|
|
||||||
}}
|
|
||||||
|
|
||||||
go ws.SendEvent(ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *App) songInfoEvent(event string) (ev Event, err error) {
|
|
||||||
ev.Event = event
|
|
||||||
|
|
||||||
status, err := app.mpd.Status()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cur, err := app.mpd.CurrentSong()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
info := new(SongInfo)
|
|
||||||
|
|
||||||
if status["state"] != "play" {
|
|
||||||
info.Pause = true
|
|
||||||
ev.Payload = info
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
duration, ok := status["duration"]
|
|
||||||
if ok && duration != "" {
|
|
||||||
var slen float64
|
|
||||||
slen, err = strconv.ParseFloat(duration, 64)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
info.Length = time.Duration(slen * float64(time.Second)).Milliseconds()
|
|
||||||
}
|
|
||||||
|
|
||||||
elapsed, ok := status["elapsed"]
|
|
||||||
if ok && elapsed != "" {
|
|
||||||
var spos float64
|
|
||||||
spos, err = strconv.ParseFloat(elapsed, 64)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
info.Position = time.Duration(spos * float64(time.Second)).Milliseconds()
|
|
||||||
}
|
|
||||||
|
|
||||||
album, ok := cur["Album"]
|
|
||||||
if ok {
|
|
||||||
var plid uuid.UUID
|
|
||||||
plid, err = uuid.ParseBytes([]byte(album))
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var pl *Playlist
|
|
||||||
pl, err = app.GetPlaylist(plid)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
info.Playlist = pl.Id
|
|
||||||
info.PlaylistName = pl.Title
|
|
||||||
}
|
|
||||||
|
|
||||||
title, ok := cur["Title"]
|
|
||||||
if ok {
|
|
||||||
info.Title = title
|
|
||||||
}
|
|
||||||
|
|
||||||
artist, ok := cur["Artist"]
|
|
||||||
if ok {
|
|
||||||
info.Channel = artist
|
|
||||||
}
|
|
||||||
|
|
||||||
location, ok := cur["Location"]
|
|
||||||
if ok {
|
|
||||||
info.Song = location
|
|
||||||
}
|
|
||||||
|
|
||||||
ev.Payload = *info
|
|
||||||
|
|
||||||
return ev, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) ambiancePlay(payload ...interface{}) {
|
func (app *App) ambiancePlay(payload ...interface{}) {
|
||||||
|
@ -244,11 +79,11 @@ func (app *App) ambiancePlay(payload ...interface{}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var id string
|
var fn string
|
||||||
switch data := payload[0].(type) {
|
switch data := payload[0].(type) {
|
||||||
case json.RawMessage:
|
case json.RawMessage:
|
||||||
var err error
|
var err error
|
||||||
err = json.Unmarshal(data, &id)
|
err = json.Unmarshal(data, &fn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return
|
return
|
||||||
|
@ -258,62 +93,44 @@ func (app *App) ambiancePlay(payload ...interface{}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
amb, err := GetAmbiance(id)
|
f, err := os.Open(fmt.Sprintf("./ambiance/%s.mp3", fn))
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = app.mpd.Partition("ambiance")
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(filepath.Join(cwd, amb.Path))
|
|
||||||
err = app.mpd.Add(filepath.Join(cwd, amb.Path))
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = app.mpd.Play(0)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = app.mpd.Partition("default")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
app.curamb = amb
|
play, _, err := mp3.Decode(f)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
ev := Event{"ambiance_play", map[string]string{
|
discordspeaker.Pause(false)
|
||||||
"id": id,
|
discordspeaker.Lock()
|
||||||
}}
|
app.ambiance.Clear()
|
||||||
|
loop := beep.Loop(-1, play)
|
||||||
|
app.ambiance.Add(loop)
|
||||||
|
discordspeaker.Unlock()
|
||||||
|
|
||||||
go ws.SendEvent(ev)
|
msg := make(map[string]interface{})
|
||||||
|
out := make(map[string]interface{})
|
||||||
|
|
||||||
|
app.curamb = fn
|
||||||
|
|
||||||
|
msg["event"] = "ambiance_play"
|
||||||
|
out["type"] = fn
|
||||||
|
msg["payload"] = out
|
||||||
|
ws_msg <- msg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) ambianceStop(payload ...interface{}) {
|
func (app *App) ambianceStop(payload ...interface{}) {
|
||||||
log.Println("Stopping ambiance")
|
log.Println("Stopping ambiance")
|
||||||
|
discordspeaker.Lock()
|
||||||
|
app.ambiance.Clear()
|
||||||
|
discordspeaker.Unlock()
|
||||||
|
|
||||||
err := app.mpd.Partition("ambiance")
|
msg := make(map[string]interface{})
|
||||||
if err != nil {
|
msg["event"] = "ambiance_stop"
|
||||||
log.Fatal(err)
|
ws_msg <- msg
|
||||||
}
|
|
||||||
|
|
||||||
app.mpd.Stop()
|
|
||||||
app.mpd.Clear()
|
|
||||||
|
|
||||||
err = app.mpd.Partition("default")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
go ws.SendEvent(Event{"ambiance_stop", nil})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) ambianceAdd(payload ...interface{}) {
|
func (app *App) ambianceAdd(payload ...interface{}) {
|
||||||
|
@ -344,89 +161,87 @@ func (app *App) ambianceAdd(payload ...interface{}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
amb, err := AddAmbiance(amburl, ambtitle)
|
err := DownloadAmbiance(amburl, ambtitle)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ev := Event{"ambiance_add", map[string]string{
|
msg := make(map[string]interface{})
|
||||||
"title": amb.Title,
|
out := make(map[string]interface{})
|
||||||
"id": amb.Id,
|
msg["event"] = "ambiance_add"
|
||||||
}}
|
out["type"] = ambtitle
|
||||||
|
msg["payload"] = out
|
||||||
go ws.SendEvent(ev)
|
ws_msg <- msg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) songPosition(payload ...interface{}) {
|
func (app *App) songPosition(payload ...interface{}) {
|
||||||
status, err := app.mpd.Status()
|
if !app.queue.IsPlaying() {
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if status["state"] == "stop" {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Do(func() {
|
l.Do(func() {
|
||||||
|
msg := make(map[string]interface{})
|
||||||
|
out := make(map[string]interface{})
|
||||||
|
|
||||||
slen, _ := strconv.ParseFloat(status["duration"], 64)
|
msg["event"] = "song_position"
|
||||||
spos, _ := strconv.ParseFloat(status["elapsed"], 64)
|
out["len"] = app.queue.Len()
|
||||||
|
out["position"] = app.queue.Position()
|
||||||
|
|
||||||
ev := Event{"song_position", map[string]int64{
|
msg["payload"] = out
|
||||||
"len": time.Duration(slen * float64(time.Second)).Milliseconds(),
|
ws_msg <- msg
|
||||||
"position": time.Duration(spos * float64(time.Second)).Milliseconds(),
|
|
||||||
}}
|
|
||||||
|
|
||||||
go ws.SendEvent(ev)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) songInfo(payload ...interface{}) {
|
func (app *App) checkTimeleft(payload ...interface{}) {
|
||||||
log.Println("song_info event received")
|
if !app.queue.IsPlaying() {
|
||||||
|
|
||||||
ev, err := app.songInfoEvent("song_info")
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go ws.SendEvent(ev)
|
timeleft := app.queue.Len() - app.queue.Position()
|
||||||
|
if timeleft <= 10000 && timeleft > 0 && !app.next {
|
||||||
|
app.events.Emit("preload_song")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *App) songInfo(payload ...interface{}) {
|
||||||
|
log.Println("song_over event received")
|
||||||
|
|
||||||
|
msg := app.songInfoEvent("song_info")
|
||||||
|
ws_msg <- msg
|
||||||
|
|
||||||
|
app.next = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) stop(payload ...interface{}) {
|
func (app *App) stop(payload ...interface{}) {
|
||||||
log.Println("stop event received")
|
log.Println("stop event received")
|
||||||
|
discordspeaker.Lock()
|
||||||
|
app.queue.Reset()
|
||||||
|
discordspeaker.Unlock()
|
||||||
|
|
||||||
app.plmutex.Lock()
|
msg := make(map[string]interface{})
|
||||||
if app.plcancel != nil {
|
msg["event"] = "stop"
|
||||||
app.plcancel()
|
|
||||||
}
|
ws_msg <- msg
|
||||||
app.mpd.Stop()
|
|
||||||
app.plmutex.Unlock()
|
|
||||||
|
|
||||||
go ws.SendEvent(Event{"stop", nil})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) prevSong(payload ...interface{}) {
|
func (app *App) prevSong(payload ...interface{}) {
|
||||||
log.Println("prev_song event received")
|
log.Println("prev_song event received")
|
||||||
app.plmutex.Lock()
|
|
||||||
err := app.mpd.Previous()
|
app.queue.Prev()
|
||||||
app.plmutex.Unlock()
|
|
||||||
if err != nil {
|
msg := app.songInfoEvent("song_info")
|
||||||
log.Println(err)
|
ws_msg <- msg
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) nextSong(payload ...interface{}) {
|
func (app *App) nextSong(payload ...interface{}) {
|
||||||
log.Println("next_song event received")
|
log.Println("next_song event received")
|
||||||
|
|
||||||
app.plmutex.Lock()
|
app.queue.Next()
|
||||||
err := app.mpd.Next()
|
|
||||||
app.plmutex.Unlock()
|
msg := app.songInfoEvent("song_info")
|
||||||
if err != nil {
|
ws_msg <- msg
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) addPlaylist(payload ...interface{}) {
|
func (app *App) addPlaylist(payload ...interface{}) {
|
||||||
|
@ -469,7 +284,7 @@ func (app *App) addPlaylist(payload ...interface{}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = ytdl_client.GetPlaylist(plurl)
|
_, err = app.Playlist(plid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error getting youtube playlist info,", plid)
|
log.Println("Error getting youtube playlist info,", plid)
|
||||||
}
|
}
|
||||||
|
@ -479,12 +294,12 @@ func (app *App) addPlaylist(payload ...interface{}) {
|
||||||
log.Println("Error getting youtube playlist info,", plid)
|
log.Println("Error getting youtube playlist info,", plid)
|
||||||
}
|
}
|
||||||
|
|
||||||
ev := Event{"new_playlist", map[string]string{
|
msg := make(map[string]interface{})
|
||||||
"url": id.String(),
|
|
||||||
"title": pltitle,
|
|
||||||
}}
|
|
||||||
|
|
||||||
go ws.SendEvent(ev)
|
msg["event"] = "new_playlist"
|
||||||
|
msg["payload"] = map[string]string{"url": id.String(), "title": pltitle}
|
||||||
|
|
||||||
|
ws_msg <- msg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) loadPlaylist(payload ...interface{}) {
|
func (app *App) loadPlaylist(payload ...interface{}) {
|
||||||
|
@ -520,88 +335,57 @@ func (app *App) loadPlaylist(payload ...interface{}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
list, err := ytdl_client.GetPlaylist(pl.Url)
|
discordspeaker.Pause(false)
|
||||||
|
list, err := app.Playlist(pl.Url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error getting playlist info,", id)
|
log.Println("Error getting playlist info,", id)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rand.Shuffle(len(list.Videos), func(i, j int) { list.Videos[i], list.Videos[j] = list.Videos[j], list.Videos[i] })
|
list, err = ShufflePlaylist(list)
|
||||||
|
if err != nil {
|
||||||
app.plmutex.Lock()
|
log.Println("Unable to shuffle playlist")
|
||||||
if app.plcancel != nil {
|
return
|
||||||
app.plcancel()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.mpd.Stop()
|
app.queue.Reset()
|
||||||
app.mpd.Clear()
|
|
||||||
app.plmutex.Unlock()
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
app.plcancel = cancel
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer cancel()
|
for _, vid := range list {
|
||||||
|
ytinfo, err := app.Video(vid)
|
||||||
for _, ytinfo := range list.Videos {
|
if err != nil {
|
||||||
log.Printf("Adding %s (%s - %s)", ytinfo.ID, ytinfo.Author, ytinfo.Title)
|
log.Println(err)
|
||||||
|
continue
|
||||||
// Run as a local function so we can defer the mutex unlock incase one of these errors.
|
|
||||||
ok := func() (ok bool) {
|
|
||||||
app.plmutex.Lock()
|
|
||||||
defer app.plmutex.Unlock()
|
|
||||||
|
|
||||||
if ctx.Err() != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
ok = true
|
|
||||||
|
|
||||||
// state:stop
|
|
||||||
songid, err := app.mpd.AddID("http://localhost:"+config.GetString("web.port")+"/youtube/"+ytinfo.ID, 0)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mpdcmd := app.mpd.Command("%s %d %s %s", mpd.Quoted("addtagid"), songid, mpd.Quoted("artist"), ytinfo.Author)
|
|
||||||
err = mpdcmd.OK()
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mpdcmd = app.mpd.Command("%s %d %s %s", mpd.Quoted("addtagid"), songid, mpd.Quoted("title"), ytinfo.Title)
|
|
||||||
err = mpdcmd.OK()
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mpdcmd = app.mpd.Command("%s %d %s %s", mpd.Quoted("addtagid"), songid, mpd.Quoted("location"), ytinfo.ID)
|
|
||||||
err = mpdcmd.OK()
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mpdcmd = app.mpd.Command("%s %d %s %s", mpd.Quoted("addtagid"), songid, mpd.Quoted("album"), pl.Id.String())
|
|
||||||
err = mpdcmd.OK()
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
app.mpd.Play(-1)
|
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
return
|
|
||||||
}()
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, yt, err := app.cache.GetOrCreateBytes(vid+".txt", func() ([]byte, error) {
|
||||||
|
uri, err := NewYTdl(vid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return uri, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gopxl/beep"
|
"github.com/faiface/beep"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PCM struct {
|
type PCM struct {
|
||||||
|
|
87
go.mod
87
go.mod
|
@ -1,40 +1,32 @@
|
||||||
module dndmusicbot
|
module dndmusicbot
|
||||||
|
|
||||||
go 1.21
|
go 1.19
|
||||||
|
|
||||||
toolchain go1.21.3
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/IzumiSy/go-fdkaac v0.0.0-20220502080852-c56d1bb3e32d
|
github.com/bwmarrin/discordgo v0.26.1
|
||||||
github.com/bitly/go-simplejson v0.5.1
|
github.com/dpup/gohubbub v0.0.0-20140517235056-2dc6969d22d8
|
||||||
github.com/davecheney/xattr v0.0.0-20151008032638-dc6dbbe49f0b
|
github.com/faiface/beep v1.1.0
|
||||||
github.com/diamondburned/arikawa/v3 v3.2.1-0.20230320210521-82c55dffac71
|
|
||||||
github.com/gohugoio/hugo v0.106.0
|
github.com/gohugoio/hugo v0.106.0
|
||||||
github.com/golang-jwt/jwt/v5 v5.0.0-rc.2
|
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/gopxl/beep v1.1.0
|
|
||||||
github.com/gorilla/sessions v1.1.1
|
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/grafov/bcast v0.0.0-20190217190352-1447f067e08d
|
github.com/grafov/bcast v0.0.0-20190217190352-1447f067e08d
|
||||||
github.com/grafov/m3u8 v0.12.0
|
|
||||||
github.com/jackc/pgx/v5 v5.1.0
|
github.com/jackc/pgx/v5 v5.1.0
|
||||||
github.com/julienschmidt/httprouter v1.3.0
|
github.com/julienschmidt/httprouter v1.3.0
|
||||||
github.com/kataras/go-events v0.0.3
|
github.com/kataras/go-events v0.0.3
|
||||||
github.com/kkdai/youtube/v2 v2.9.0
|
|
||||||
github.com/markbates/goth v1.76.1
|
|
||||||
github.com/peterhellberg/link v1.2.0
|
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/romantomjak/shoutcast v1.2.0
|
github.com/r3labs/sse/v2 v2.8.2
|
||||||
github.com/spf13/afero v1.9.5
|
github.com/sosodev/duration v1.0.1
|
||||||
github.com/spf13/viper v1.16.0
|
github.com/spf13/afero v1.9.3
|
||||||
github.com/steino/gompd/v2 v2.3.1
|
github.com/spf13/viper v1.14.0
|
||||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
|
github.com/tidwall/gjson v1.14.3
|
||||||
golang.org/x/net v0.15.0
|
golang.org/x/time v0.2.0
|
||||||
golang.org/x/time v0.3.0
|
google.golang.org/api v0.103.0
|
||||||
gopkg.in/hraban/opus.v2 v2.0.0-20230925203106-0188a62cb302
|
layeh.com/gopus v0.0.0-20210501142526-1ee02d434e32
|
||||||
)
|
)
|
||||||
|
|
||||||
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/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69 // indirect
|
||||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||||
|
@ -47,51 +39,50 @@ require (
|
||||||
github.com/bep/overlayfs v0.6.0 // indirect
|
github.com/bep/overlayfs v0.6.0 // indirect
|
||||||
github.com/clbanning/mxj/v2 v2.5.7 // indirect
|
github.com/clbanning/mxj/v2 v2.5.7 // indirect
|
||||||
github.com/cli/safeexec v1.0.0 // indirect
|
github.com/cli/safeexec v1.0.0 // indirect
|
||||||
github.com/dlclark/regexp2 v1.10.0 // indirect
|
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||||
github.com/dop251/goja v0.0.0-20230828202809-3dbe69dd2b8e // indirect
|
|
||||||
github.com/fhs/gompd/v2 v2.3.0 // indirect
|
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
|
||||||
github.com/gobwas/glob v0.2.3 // indirect
|
github.com/gobwas/glob v0.2.3 // indirect
|
||||||
github.com/gohugoio/locales v0.14.0 // indirect
|
github.com/gohugoio/locales v0.14.0 // indirect
|
||||||
github.com/gohugoio/localescompressed v1.0.1 // indirect
|
github.com/gohugoio/localescompressed v1.0.1 // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/google/pprof v0.0.0-20230907193218-d3ddc7976beb // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/gorilla/context v1.1.1 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect
|
||||||
github.com/gorilla/mux v1.6.2 // indirect
|
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
|
||||||
github.com/gorilla/schema v1.2.0 // indirect
|
github.com/hajimehoshi/go-mp3 v0.3.0 // indirect
|
||||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/icza/bitio v1.1.0 // indirect
|
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||||
github.com/jdkato/prose v1.2.1 // indirect
|
github.com/jdkato/prose v1.2.1 // indirect
|
||||||
github.com/kyokomi/emoji/v2 v2.2.10 // indirect
|
github.com/kyokomi/emoji/v2 v2.2.10 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.6 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||||
github.com/mewkiz/flac v1.0.9 // indirect
|
|
||||||
github.com/mewkiz/pkg v0.0.0-20230226050401-4010bf0fec14 // indirect
|
|
||||||
github.com/mitchellh/hashstructure v1.1.0 // indirect
|
github.com/mitchellh/hashstructure v1.1.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/niklasfasching/go-org v1.6.5 // indirect
|
github.com/niklasfasching/go-org v1.6.5 // indirect
|
||||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||||
github.com/rivo/uniseg v0.4.4 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.11.0 // indirect
|
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||||
github.com/spf13/cast v1.5.1 // indirect
|
github.com/spf13/cast v1.5.0 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.4.1 // indirect
|
||||||
github.com/tdewolff/parse/v2 v2.6.4 // 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
|
github.com/yuin/goldmark v1.5.3 // indirect
|
||||||
golang.org/x/crypto v0.13.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
golang.org/x/mod v0.9.0 // indirect
|
golang.org/x/crypto v0.2.0 // indirect
|
||||||
golang.org/x/oauth2 v0.7.0 // indirect
|
golang.org/x/net v0.2.0 // indirect
|
||||||
golang.org/x/sys v0.12.0 // indirect
|
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
|
||||||
golang.org/x/text v0.13.0 // indirect
|
golang.org/x/sys v0.2.0 // indirect
|
||||||
|
golang.org/x/text v0.4.0 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/protobuf v1.30.0 // indirect
|
google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect
|
||||||
|
google.golang.org/grpc v1.50.1 // indirect
|
||||||
|
google.golang.org/protobuf v1.28.1 // indirect
|
||||||
|
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
|
||||||
gopkg.in/fatih/set.v0 v0.2.1 // indirect
|
gopkg.in/fatih/set.v0 v0.2.1 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
|
277
go.sum
277
go.sum
|
@ -14,18 +14,23 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV
|
||||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||||
cloud.google.com/go v0.67.0/go.mod h1:YNan/mUhNZFrYUor0vqrsQ0Ffl7Xtm/ACOy/vsTS858=
|
|
||||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||||
|
cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y=
|
||||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||||
|
cloud.google.com/go/compute v1.12.1 h1:gKVJMEyqV5c/UnpzjjQbo3Rjvvqpr9B1DFSbJC4OXr0=
|
||||||
|
cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU=
|
||||||
|
cloud.google.com/go/compute/metadata v0.2.1 h1:efOwf5ymceDhK6PKMnnrTHP4pppY5L22mle96M1yP48=
|
||||||
|
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
|
||||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||||
|
cloud.google.com/go/longrunning v0.1.1 h1:y50CXG4j0+qvEukslYFBCrzaXX0qpFbBzc3PchSu/LE=
|
||||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||||
|
@ -42,8 +47,6 @@ github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZd
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
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/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/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||||
github.com/IzumiSy/go-fdkaac v0.0.0-20220502080852-c56d1bb3e32d h1:2Fn0vK/lH/pI2/TtUTFgb3iKkbw7TDlxBLglELToDRo=
|
|
||||||
github.com/IzumiSy/go-fdkaac v0.0.0-20220502080852-c56d1bb3e32d/go.mod h1:jmxJwIPbDUrofy2H3d28lYzz+RL9qt8GltDAAJ/mlb0=
|
|
||||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
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/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 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||||
|
@ -51,7 +54,6 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko
|
||||||
github.com/alecthomas/chroma/v2 v2.3.0 h1:83xfxrnjv8eK+Cf8qZDzNo3PPF9IbTWHs7z28GY6D0U=
|
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/chroma/v2 v2.3.0/go.mod h1:mZxeWZlxP2Dy+/8cBob2PYd8O2DwNAzave5AY7A2eQw=
|
||||||
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
|
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
|
||||||
github.com/alecthomas/repr v0.1.0/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
|
||||||
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
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/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 h1:vfOA6+wVb6pPQEiXow9f/too92vNTLe9MuwO13PfI0M=
|
||||||
|
@ -59,30 +61,23 @@ github.com/bep/clock v0.3.0/go.mod h1:6Gz2lapnJ9vxpvPxQ2u6FcXFRoj4kkiqQ6pm0ERZlw
|
||||||
github.com/bep/debounce v1.2.0 h1:wXds8Kq8qRfwAOpAxHrJDbCXgC5aHSzgQb/0gKsHQqo=
|
github.com/bep/debounce v1.2.0 h1:wXds8Kq8qRfwAOpAxHrJDbCXgC5aHSzgQb/0gKsHQqo=
|
||||||
github.com/bep/debounce v1.2.0/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
github.com/bep/debounce v1.2.0/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
||||||
github.com/bep/gitmap v1.1.2 h1:zk04w1qc1COTZPPYWDQHvns3y1afOsdRfraFQ3qI840=
|
github.com/bep/gitmap v1.1.2 h1:zk04w1qc1COTZPPYWDQHvns3y1afOsdRfraFQ3qI840=
|
||||||
github.com/bep/gitmap v1.1.2/go.mod h1:g9VRETxFUXNWzMiuxOwcudo6DfZkW9jOsOW0Ft4kYaY=
|
|
||||||
github.com/bep/goat v0.5.0 h1:S8jLXHCVy/EHIoCY+btKkmcxcXFd34a0Q63/0D4TKeA=
|
github.com/bep/goat v0.5.0 h1:S8jLXHCVy/EHIoCY+btKkmcxcXFd34a0Q63/0D4TKeA=
|
||||||
github.com/bep/goat v0.5.0/go.mod h1:Md9x7gRxiWKs85yHlVTvHQw9rg86Bm+Y4SuYE8CTH7c=
|
|
||||||
github.com/bep/godartsass v0.14.0 h1:pPb6XkpyDEppS+wK0veh7OXDQc4xzOJI9Qcjb743UeQ=
|
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/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 h1:pjtXr00IJZZaOdfryNa9wARTB3Q0BmxC3/V1KNcgyTw=
|
||||||
github.com/bep/golibsass v1.1.0/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA=
|
github.com/bep/golibsass v1.1.0/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA=
|
||||||
github.com/bep/gowebp v0.2.0 h1:ZVfK8i9PpZqKHEmthQSt3qCnnHycbLzBPEsVtk2ch2Q=
|
github.com/bep/gowebp v0.2.0 h1:ZVfK8i9PpZqKHEmthQSt3qCnnHycbLzBPEsVtk2ch2Q=
|
||||||
github.com/bep/gowebp v0.2.0/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2AgI=
|
|
||||||
github.com/bep/overlayfs v0.6.0 h1:sgLcq/qtIzbaQNl2TldGXOkHvqeZB025sPvHOQL+DYo=
|
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/overlayfs v0.6.0/go.mod h1:NFjSmn3kCqG7KX2Lmz8qT8VhPPCwZap3UNogXawoQHM=
|
||||||
github.com/bep/tmc v0.5.1 h1:CsQnSC6MsomH64gw0cT5f+EwQDcvZz4AazKunFwTpuI=
|
github.com/bep/tmc v0.5.1 h1:CsQnSC6MsomH64gw0cT5f+EwQDcvZz4AazKunFwTpuI=
|
||||||
github.com/bep/tmc v0.5.1/go.mod h1:tGYHN8fS85aJPhDLgXETVKp+PR382OvFi2+q2GkGsq0=
|
|
||||||
github.com/bep/workers v1.0.0 h1:U+H8YmEaBCEaFZBst7GcRVEoqeRC9dzH2dWOwGmOchg=
|
github.com/bep/workers v1.0.0 h1:U+H8YmEaBCEaFZBst7GcRVEoqeRC9dzH2dWOwGmOchg=
|
||||||
github.com/bep/workers v1.0.0/go.mod h1:7kIESOB86HfR2379pwoMWNy8B50D7r99fRLUyPSNyCs=
|
github.com/bep/workers v1.0.0/go.mod h1:7kIESOB86HfR2379pwoMWNy8B50D7r99fRLUyPSNyCs=
|
||||||
github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow=
|
github.com/bwmarrin/discordgo v0.26.1 h1:AIrM+g3cl+iYBr4yBxCBp9tD9jR3K7upEjl0d89FRkE=
|
||||||
github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q=
|
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/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/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
|
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
|
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
|
||||||
github.com/clbanning/mxj/v2 v2.5.7 h1:7q5lvUpaPF/WOkqgIDiwjBJaznaLCCBd78pi8ZyAnE0=
|
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/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 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI=
|
||||||
|
@ -96,23 +91,11 @@ github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozb
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecheney/xattr v0.0.0-20151008032638-dc6dbbe49f0b h1:a/CjIrvEH2NkUUIo4sqWIw+h3E63ttmS8L8Vx3ZaLS0=
|
|
||||||
github.com/davecheney/xattr v0.0.0-20151008032638-dc6dbbe49f0b/go.mod h1:Gc/R1HBRJIEElnD4PGXGQZQYMb14oPbvTovm6WeuAvk=
|
|
||||||
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
|
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE=
|
|
||||||
github.com/diamondburned/arikawa/v3 v3.2.1-0.20230320210521-82c55dffac71 h1:1Wqec9jTprYuWfx/XiJVaRkH5RCkyrgnHdFj0O5NFn4=
|
|
||||||
github.com/diamondburned/arikawa/v3 v3.2.1-0.20230320210521-82c55dffac71/go.mod h1:+ifmDonP/JdBiUOzZmVReEjPTHDUSkyqqRRmjSf9NE8=
|
|
||||||
github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc=
|
github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc=
|
||||||
github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI=
|
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
||||||
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||||
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
github.com/dpup/gohubbub v0.0.0-20140517235056-2dc6969d22d8 h1:t1Ox7k2+GSzIv3fihjV7YFGb40nb/e2oyrTM/ngbzbA=
|
||||||
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
|
github.com/dpup/gohubbub v0.0.0-20140517235056-2dc6969d22d8/go.mod h1:QqXVl9BAyVoWIZE4oA9XfkwCjQ3JaajiX4vq7Zh8Vzs=
|
||||||
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
|
||||||
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
|
|
||||||
github.com/dop251/goja v0.0.0-20230828202809-3dbe69dd2b8e h1:UvQD6hTSfeM6hhTQ24Dlw2RppP05W7SWbWb6kubJAog=
|
|
||||||
github.com/dop251/goja v0.0.0-20230828202809-3dbe69dd2b8e/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
|
|
||||||
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
|
|
||||||
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
@ -120,55 +103,42 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
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/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/evanw/esbuild v0.15.14 h1:J/cqgL3yfj/HDHDo9txKAqyzTBYfAMuqCknkS2jhX24=
|
||||||
github.com/evanw/esbuild v0.15.14/go.mod h1:iINY06rn799hi48UqEnaQvVfZWe6W9bET78LbvN8VWk=
|
github.com/faiface/beep v1.1.0 h1:A2gWP6xf5Rh7RG/p9/VAW2jRSDEGQm5sbOb38sf5d4c=
|
||||||
github.com/fhs/gompd/v2 v2.3.0 h1:wuruUjmOODRlJhrYx73rJnzS7vTSXSU7pWmZtM3VPE0=
|
github.com/faiface/beep v1.1.0/go.mod h1:6I8p6kK2q4opL/eWb+kAkk38ehnTunWeToJB+s51sT4=
|
||||||
github.com/fhs/gompd/v2 v2.3.0/go.mod h1:nNdZtcpD5VpmzZbRl5rV6RhxeMmAWTxEsSIMBkmMIy4=
|
|
||||||
github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
|
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.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.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
|
||||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||||
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
|
||||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
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/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||||
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
|
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/getkin/kin-openapi v0.108.0 h1:EYf0GtsKa4hQNIlplGS+Au7NEfGQ1F7MoHD2kcVevPQ=
|
||||||
github.com/getkin/kin-openapi v0.108.0/go.mod h1:QtwUNt0PAAgIIBEvFWYfB7dfngxtAaqCX1zYHMZDeK8=
|
|
||||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
|
||||||
github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs=
|
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/riff v1.0.0/go.mod h1:l3cQwc85y79NQFCRB7TiPoNiaijp6q8Z0Uv38rVG498=
|
||||||
github.com/go-audio/wav v1.0.0/go.mod h1:3yoReyQOsiARkvPl3ERCi8JFjihzG6WhjYpZCf5zAWE=
|
github.com/go-audio/wav v1.0.0/go.mod h1:3yoReyQOsiARkvPl3ERCi8JFjihzG6WhjYpZCf5zAWE=
|
||||||
github.com/go-audio/wav v1.1.0/go.mod h1:mpe9qfwbScEbkd8uybLuIpTgHyrISw/OTuvjUW2iGtE=
|
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
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-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-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/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
|
||||||
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
||||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
|
||||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
|
|
||||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
|
||||||
github.com/gobuffalo/flect v0.3.0 h1:erfPWM+K1rFNIQeRPdeEXxo8yFr/PO17lhRnS8FUrtk=
|
github.com/gobuffalo/flect v0.3.0 h1:erfPWM+K1rFNIQeRPdeEXxo8yFr/PO17lhRnS8FUrtk=
|
||||||
github.com/gobuffalo/flect v0.3.0/go.mod h1:5pf3aGnsvqvCj50AVni7mJJF8ICxGZ8HomberC3pXLE=
|
|
||||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
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/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||||
github.com/goccy/go-json v0.9.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
|
||||||
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20210430103248-4c28c89f8013 h1:Nj29Qbkt0bZ/bJl8eccfxQp3NlU/0IW1v9eyYtQ53XQ=
|
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20210430103248-4c28c89f8013 h1:Nj29Qbkt0bZ/bJl8eccfxQp3NlU/0IW1v9eyYtQ53XQ=
|
||||||
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20210430103248-4c28c89f8013/go.mod h1:3Ltoo9Banwq0gOtcOwxuHG6omk+AwsQPADyw2vQYOJQ=
|
|
||||||
github.com/gohugoio/hugo v0.106.0 h1:MDTmX2l1/zTh0HS4CADta4a/b63aiyj6yC2WW4A+UR0=
|
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/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 h1:Q0gpsZwfv7ATHMbcTNepFd59H7GoykzWJIxi113XGDc=
|
||||||
github.com/gohugoio/locales v0.14.0/go.mod h1:ip8cCAv/cnmVLzzXtiTpPwgJ4xhKZranqNqtoIu0b/4=
|
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 h1:KTYMi8fCWYLswFyJAeOtuk/EkXR/KPTHHNN9OS+RTxo=
|
||||||
github.com/gohugoio/localescompressed v1.0.1/go.mod h1:jBF6q8D7a0vaEmcWPNcAjUZLJaIVNiwvM3WlmTvooB0=
|
github.com/gohugoio/localescompressed v1.0.1/go.mod h1:jBF6q8D7a0vaEmcWPNcAjUZLJaIVNiwvM3WlmTvooB0=
|
||||||
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
|
||||||
github.com/golang-jwt/jwt/v5 v5.0.0-rc.2 h1:hXPcSazn8wKOfSb9y2m1bdgUMlDxVDarxh3lJVbC6JE=
|
|
||||||
github.com/golang-jwt/jwt/v5 v5.0.0-rc.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
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-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
|
@ -191,8 +161,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
@ -203,11 +173,11 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
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.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.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.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
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=
|
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
|
@ -218,42 +188,27 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
|
||||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20200905233945-acf8798be1f7/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
|
||||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
|
|
||||||
github.com/google/pprof v0.0.0-20230907193218-d3ddc7976beb h1:LCMfzVg3sflxTs4UvuP4D8CkoZnfHLe2qzqgDn/4OHs=
|
|
||||||
github.com/google/pprof v0.0.0-20230907193218-d3ddc7976beb/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
|
||||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||||
github.com/gopxl/beep v1.1.0 h1:YBfaDhZh4bC6IJfDsEi/8wmtUanir0dMIxpRu3F6Yeo=
|
|
||||||
github.com/gopxl/beep v1.1.0/go.mod h1:N5ClU2N8ESeO6ibbz5UThPRFpdEgbU9G60CLZ6u3v9s=
|
|
||||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
|
||||||
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
|
|
||||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
|
||||||
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1/go.mod h1:YeAe0gNeiNT5hoiZRI4yiOky6jVdNvfO2N6Kav/HmxY=
|
|
||||||
github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
|
|
||||||
github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
|
||||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
|
||||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
|
||||||
github.com/gorilla/sessions v1.1.1 h1:YMDmfaK68mUixINzY/XjscuJ47uXFWSSHzFbBQM0PrE=
|
|
||||||
github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
|
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
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 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/grafov/bcast v0.0.0-20190217190352-1447f067e08d/go.mod h1:RInr+B3/Tx70hYm0rpNPMTD7vH0pBG5ny/JsHAs2KcQ=
|
||||||
github.com/grafov/m3u8 v0.12.0 h1:T6iTwTsSEtMcwkayef+FJO8kj+Sglr4Lh81Zj8Ked/4=
|
|
||||||
github.com/grafov/m3u8 v0.12.0/go.mod h1:nqzOkfBiZJENr52zTVd/Dcl03yzphIMbJqkXGu+u080=
|
|
||||||
github.com/hairyhenderson/go-codeowners v0.2.3-0.20201026200250-cdc7c0759690 h1:XWjCrg/HJRLZCbvsUxS5R/9JhwiiwNctEsRvZ1Vjz5k=
|
github.com/hairyhenderson/go-codeowners v0.2.3-0.20201026200250-cdc7c0759690 h1:XWjCrg/HJRLZCbvsUxS5R/9JhwiiwNctEsRvZ1Vjz5k=
|
||||||
github.com/hairyhenderson/go-codeowners v0.2.3-0.20201026200250-cdc7c0759690/go.mod h1:8Qu9UmnhCRunfRv365Z3w+mT/WfLGKJiK+vugY9qNCU=
|
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/go-mp3 v0.3.0/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
|
||||||
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
|
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
|
||||||
github.com/hajimehoshi/oto v0.7.1/go.mod h1:wovJ8WWMfFKvP587mhHgot/MBr4DnNy9m6EepeVGnos=
|
github.com/hajimehoshi/oto v0.7.1/go.mod h1:wovJ8WWMfFKvP587mhHgot/MBr4DnNy9m6EepeVGnos=
|
||||||
|
@ -263,96 +218,65 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
|
||||||
github.com/icza/bitio v1.0.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A=
|
github.com/icza/bitio v1.0.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A=
|
||||||
github.com/icza/bitio v1.1.0 h1:ysX4vtldjdi3Ygai5m1cWy4oLkhWTAi+SyO6HC8L9T0=
|
|
||||||
github.com/icza/bitio v1.1.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A=
|
|
||||||
github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6 h1:8UsGZ2rr2ksmEru6lToqnXgA8Mz1DP11X4zSJ159C3k=
|
|
||||||
github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA=
|
github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA=
|
||||||
github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc=
|
github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc=
|
||||||
github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
|
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
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/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 h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
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 h1:Z7pLKUb65HK6m18No8GGKT87K34NhIIEHa86rRdjxbU=
|
||||||
github.com/jackc/pgx/v5 v5.1.0/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk=
|
github.com/jackc/pgx/v5 v5.1.0/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk=
|
||||||
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4=
|
|
||||||
github.com/jdkato/prose v1.2.1 h1:Fp3UnJmLVISmlc57BgKUzdjr0lOtjqTZicL3PaYy6cU=
|
github.com/jdkato/prose v1.2.1 h1:Fp3UnJmLVISmlc57BgKUzdjr0lOtjqTZicL3PaYy6cU=
|
||||||
github.com/jdkato/prose v1.2.1/go.mod h1:AiRHgVagnEx2JbQRQowVBKjG0bcs/vtkGCH1dYAL1rA=
|
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/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/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0=
|
||||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
|
||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
github.com/jszwec/csvutil v1.5.1/go.mod h1:Rpu7Uu9giO9subDyMCIQfHVDuLrcaC36UA4YcJjGBkg=
|
|
||||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
github.com/kataras/go-events v0.0.3 h1:o5YK53uURXtrlg7qE/vovxd/yKOJcLuFtPQbf1rYMC4=
|
github.com/kataras/go-events v0.0.3 h1:o5YK53uURXtrlg7qE/vovxd/yKOJcLuFtPQbf1rYMC4=
|
||||||
github.com/kataras/go-events v0.0.3/go.mod h1:bFBgtzwwzrag7kQmGuU1ZaVxhK2qseYPQomXoVEMsj4=
|
github.com/kataras/go-events v0.0.3/go.mod h1:bFBgtzwwzrag7kQmGuU1ZaVxhK2qseYPQomXoVEMsj4=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/kkdai/youtube/v2 v2.9.0 h1:J7BvfIsxEyyd1MmB/75LgDvG8BGGsG9bSHpbo/qIb+8=
|
|
||||||
github.com/kkdai/youtube/v2 v2.9.0/go.mod h1:H0ntZBgaah4F0wxnEUkLa6yUeyTDDg06xFJ3tvA6gOw=
|
|
||||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
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/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
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.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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
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 h1:1z5eMVcxFifsmEoNpdeq4UahbcicgQ4FEHuzrCVwmiI=
|
||||||
github.com/kyokomi/emoji/v2 v2.2.10/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
|
github.com/kyokomi/emoji/v2 v2.2.10/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
|
||||||
github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
|
|
||||||
github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ=
|
|
||||||
github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE=
|
|
||||||
github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
|
|
||||||
github.com/lestrrat-go/jwx v1.2.21/go.mod h1:9cfxnOH7G1gN75CaJP2hKGcxFEx5sPh1abRIA/ZJVh4=
|
|
||||||
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
|
||||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
|
||||||
github.com/marekm4/color-extractor v1.2.0 h1:DCU/FXg3PlAwig7W5PRZshiX5x38k0aNPTxYZ6/fZb0=
|
github.com/marekm4/color-extractor v1.2.0 h1:DCU/FXg3PlAwig7W5PRZshiX5x38k0aNPTxYZ6/fZb0=
|
||||||
github.com/marekm4/color-extractor v1.2.0/go.mod h1:90VjmiHI6M8ez9eYUaXLdcKnS+BAOp7w+NpwBdkJmpA=
|
|
||||||
github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA=
|
|
||||||
github.com/markbates/goth v1.76.1 h1:Q2adw0e101v+DlBfxwP7OOjLGkU/pwpNMwu/RYym54w=
|
|
||||||
github.com/markbates/goth v1.76.1/go.mod h1:X6xdNgpapSENS0O35iTBBcMHoJDQDfI9bJl+APCkYMc=
|
|
||||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
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-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.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/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
|
||||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
|
||||||
github.com/mewkiz/flac v1.0.7/go.mod h1:yU74UH277dBUpqxPouHSQIar3G1X/QIclVbFahSd1pU=
|
github.com/mewkiz/flac v1.0.7/go.mod h1:yU74UH277dBUpqxPouHSQIar3G1X/QIclVbFahSd1pU=
|
||||||
github.com/mewkiz/flac v1.0.9 h1:/+wxe/2fA8YbD1kjJNhlVAyc6pWaX0XXZC4xCF5OtVY=
|
|
||||||
github.com/mewkiz/flac v1.0.9/go.mod h1:l7dt5uFY724eKVkHQtAJAQSkhpC3helU3RDxN0ESAqo=
|
|
||||||
github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2/go.mod h1:3E2FUC/qYUfM8+r9zAwpeHJzqRVVMIYnpzD/clwWxyA=
|
github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2/go.mod h1:3E2FUC/qYUfM8+r9zAwpeHJzqRVVMIYnpzD/clwWxyA=
|
||||||
github.com/mewkiz/pkg v0.0.0-20230226050401-4010bf0fec14 h1:tnAPMExbRERsyEYkmR1YjhTgDM0iqyiBYf8ojRXxdbA=
|
|
||||||
github.com/mewkiz/pkg v0.0.0-20230226050401-4010bf0fec14/go.mod h1:QYCFBiH5q6XTHEbWhR0uhR3M9qNPoD2CSQzr0g75kE4=
|
|
||||||
github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0=
|
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/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 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
|
||||||
github.com/montanaflynn/stats v0.6.3/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
github.com/montanaflynn/stats v0.6.3/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM=
|
|
||||||
github.com/muesli/smartcrop v0.3.0 h1:JTlSkmxWg/oQ1TcLDoypuirdE8Y/jzNirQeLkxpA6Oc=
|
github.com/muesli/smartcrop v0.3.0 h1:JTlSkmxWg/oQ1TcLDoypuirdE8Y/jzNirQeLkxpA6Oc=
|
||||||
github.com/muesli/smartcrop v0.3.0/go.mod h1:i2fCI/UorTfgEpPPLWiFBv4pye+YAG78RwcQLUkocpI=
|
|
||||||
github.com/neurosnap/sentences v1.0.6/go.mod h1:pg1IapvYpWCJJm/Etxeh0+gtMf1rI1STY9S7eUCPbDc=
|
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 h1:5YAIqNTdl6lAOb7lD2AyQ1RuFGPVrAKvUexphk8PGbo=
|
||||||
github.com/niklasfasching/go-org v1.6.5/go.mod h1:ybv0eGDnxylFUfFE+ySaQc734j/L3+/ChKZ/h63a2wM=
|
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 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
github.com/peterhellberg/link v1.2.0 h1:UA5pg3Gp/E0F2WdX7GERiNrPQrM1K6CVJUUWfHa4t6c=
|
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
|
||||||
github.com/peterhellberg/link v1.2.0/go.mod h1:gYfAh+oJgQu2SrZHg5hROVRQe1ICoK0/HHJTcE0edxc=
|
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.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
@ -360,32 +284,27 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/r3labs/sse/v2 v2.8.2 h1:YWZy2i2nLoD5fE3vLLTdTz/8wxIYIFp5XbLNmmrrNts=
|
||||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
github.com/r3labs/sse/v2 v2.8.2/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktEmkNJ7I=
|
||||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
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.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/romantomjak/shoutcast v1.2.0 h1:MrksaqAd2De3rwvhlkzY8eUEaRYmkvhy9OH0sqM/w74=
|
|
||||||
github.com/romantomjak/shoutcast v1.2.0/go.mod h1:U4Gem8Jbwbz/A2ml2DYOGK0tVpnfQ5sQpSY3UWXcZM4=
|
|
||||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
|
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
|
||||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
|
|
||||||
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||||
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
|
||||||
github.com/shogo82148/go-shuffle v0.0.0-20180218125048-27e6095f230d/go.mod h1:2htx6lmL0NGLHlO8ZCf+lQBGBHIbEujyywxJArf+2Yc=
|
github.com/shogo82148/go-shuffle v0.0.0-20180218125048-27e6095f230d/go.mod h1:2htx6lmL0NGLHlO8ZCf+lQBGBHIbEujyywxJArf+2Yc=
|
||||||
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
github.com/sosodev/duration v1.0.1 h1:qovz/BFb6kp30KZ4/AYZvB5Z6zANmeQja5l6W9X1w68=
|
||||||
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
github.com/sosodev/duration v1.0.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
|
||||||
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
|
||||||
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
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=
|
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
|
github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU=
|
||||||
github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
|
github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As=
|
||||||
github.com/steino/gompd/v2 v2.3.1 h1:hzhzsN18omDnDOJRzSu3KNu/dIyuaJinYE3ICK5bkWs=
|
|
||||||
github.com/steino/gompd/v2 v2.3.1/go.mod h1:6fLLeC5m6Yq/TUQ8e1Va0m+1iSOAFz++X+FbGvneCE4=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
@ -397,22 +316,27 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||||
github.com/stretchr/testify v1.7.0/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.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
||||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
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/minify/v2 v2.12.4 h1:kejsHQMM17n6/gwdw53qsi6lg0TGddZADVyQOz1KMdE=
|
||||||
github.com/tdewolff/minify/v2 v2.12.4/go.mod h1:h+SRvSIX3kwgwTFOpSckvSxgax3uy8kZTSF1Ojrr3bk=
|
|
||||||
github.com/tdewolff/parse/v2 v2.6.4 h1:KCkDvNUMof10e3QExio9OPZJT8SbdKojLBumw8YZycQ=
|
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/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 h1:8Vs0142DmPFW/bQeHRP3MV19m1gvndjUb1sn8yy74LM=
|
||||||
github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
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=
|
||||||
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
|
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||||
|
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
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.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/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.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
|
||||||
github.com/yuin/goldmark v1.5.3 h1:3HUJmBFbQW9fhQOzMgseU134xfi6hU+mjWywx5Ty+/M=
|
github.com/yuin/goldmark v1.5.3 h1:3HUJmBFbQW9fhQOzMgseU134xfi6hU+mjWywx5Ty+/M=
|
||||||
github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
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.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
@ -421,20 +345,18 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
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.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=
|
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE=
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
|
||||||
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
|
||||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
@ -445,13 +367,10 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
|
||||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
|
||||||
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
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-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-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
|
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
|
||||||
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
|
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
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-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
@ -475,9 +394,6 @@ 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.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.1/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/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
|
||||||
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
|
|
||||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -490,6 +406,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
@ -504,19 +421,15 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200927032502-5d4f70055728/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
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-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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|
||||||
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
|
||||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
@ -526,8 +439,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
|
||||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
|
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk=
|
||||||
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
|
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -539,9 +452,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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-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.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
@ -577,23 +488,14 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/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-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-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/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-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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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-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.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@ -601,18 +503,13 @@ 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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.4/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.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|
||||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE=
|
||||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
golang.org/x/time v0.2.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
@ -654,7 +551,6 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
|
||||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||||
golang.org/x/tools v0.0.0-20200929161345-d7fc70abf50f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
|
||||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
@ -662,13 +558,12 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
|
||||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/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.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
||||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
@ -685,10 +580,11 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
|
||||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||||
google.golang.org/api v0.32.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
|
||||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||||
|
google.golang.org/api v0.103.0 h1:9yuVqlu2JCvcLg9p8S3fcFLZij8EPSyvODIY1rkMizQ=
|
||||||
|
google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
@ -727,13 +623,14 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D
|
||||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20200929141702-51c3e5b607fe/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c h1:QgY/XxIAIeccR+Ca/rDdKubLIU9rcJ3xfy1DC/Wd2Oo=
|
||||||
|
google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
@ -747,10 +644,11 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
|
||||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
|
||||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
|
google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY=
|
||||||
|
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
@ -763,17 +661,16 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y=
|
||||||
|
gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/fatih/set.v0 v0.2.1 h1:Xvyyp7LXu34P0ROhCyfXkmQCAoOUKb1E2JS9I7SE5CY=
|
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/fatih/set.v0 v0.2.1/go.mod h1:5eLWEndGL4zGGemXWrKuts+wTJR0y+w+auqUJZbmyBg=
|
||||||
gopkg.in/hraban/opus.v2 v2.0.0-20230925203106-0188a62cb302 h1:xeVptzkP8BuJhoIjNizd2bRHfq9KB9HfOLZu90T04XM=
|
|
||||||
gopkg.in/hraban/opus.v2 v2.0.0-20230925203106-0188a62cb302/go.mod h1:/L5E7a21VWl8DeuCPKxQBdVG5cy+L0MRZ08B1wnqt7g=
|
|
||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
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/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/neurosnap/sentences.v1 v1.0.6/go.mod h1:YlK+SN+fLQZj+kY3r8DkGDhDr91+S3JmTb5LSxFRQo0=
|
||||||
|
@ -790,6 +687,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
layeh.com/gopus v0.0.0-20210501142526-1ee02d434e32 h1:/S1gOotFo2sADAIdSGk1sDq1VxetoCWr6f5nxOG0dpY=
|
||||||
|
layeh.com/gopus v0.0.0-20210501142526-1ee02d434e32/go.mod h1:yDtyzWZDFCVnva8NGtg38eH2Ns4J0D/6hD+MMeUGdF0=
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
|
|
|
@ -35,40 +35,6 @@ window.onload = function () {
|
||||||
const link = document.querySelector("#link")
|
const link = document.querySelector("#link")
|
||||||
const time = document.querySelector("#time")
|
const time = document.querySelector("#time")
|
||||||
const waiting = document.querySelector("#waiting")
|
const waiting = document.querySelector("#waiting")
|
||||||
const pvol = document.querySelector("#playlist-volume")
|
|
||||||
const avol = document.querySelector("#ambiance-volume")
|
|
||||||
|
|
||||||
pvol.addEventListener("change", (e) => {
|
|
||||||
e.target.nextElementSibling.value = e.target.value
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
"event": "vol_set",
|
|
||||||
"payload": {
|
|
||||||
"type": "playlist",
|
|
||||||
"vol": e.target.value
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
|
|
||||||
pvol.nextElementSibling.addEventListener("change", (e) => {
|
|
||||||
e.target.previousElementSibling.value = e.target.value
|
|
||||||
e.target.previousElementSibling.dispatchEvent(new Event('change'));
|
|
||||||
})
|
|
||||||
|
|
||||||
avol.addEventListener("change", (e) => {
|
|
||||||
e.target.nextElementSibling.value = e.target.value
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
"event": "vol_set",
|
|
||||||
"payload": {
|
|
||||||
"type": "ambiance",
|
|
||||||
"vol": e.target.value
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
|
|
||||||
avol.nextElementSibling.addEventListener("change", (e) => {
|
|
||||||
e.target.previousElementSibling.value = e.target.value
|
|
||||||
e.target.previousElementSibling.dispatchEvent(new Event('change'));
|
|
||||||
})
|
|
||||||
|
|
||||||
addInteractHandler(
|
addInteractHandler(
|
||||||
document.querySelector("input#next"), (e) => {
|
document.querySelector("input#next"), (e) => {
|
||||||
|
@ -141,81 +107,9 @@ window.onload = function () {
|
||||||
ws.onmessage = (e) => {
|
ws.onmessage = (e) => {
|
||||||
data = JSON.parse(e.data)
|
data = JSON.parse(e.data)
|
||||||
switch (data.event) {
|
switch (data.event) {
|
||||||
case "volume":
|
|
||||||
pvol.value = data.payload.playlist
|
|
||||||
pvol.nextElementSibling.value = data.payload.playlist
|
|
||||||
avol.value = data.payload.ambiance
|
|
||||||
avol.nextElementSibling.value = data.payload.ambiance
|
|
||||||
break
|
|
||||||
case "ambiance_download_start":
|
|
||||||
var progress = document.querySelector("#progressambiance progress")
|
|
||||||
progress.style.display = "initial"
|
|
||||||
progress.style.width = "100%"
|
|
||||||
progress.value = 0
|
|
||||||
break
|
|
||||||
case "ambiance_download_progress":
|
|
||||||
var progress = document.querySelector("#progressambiance progress")
|
|
||||||
progress.style.display = "initial"
|
|
||||||
progress.value = data.payload.percent
|
|
||||||
console.log(data)
|
|
||||||
break
|
|
||||||
case "ambiance_download_complete":
|
|
||||||
var progress = document.querySelector("#progressambiance progress")
|
|
||||||
progress.style.display = "initial"
|
|
||||||
progress.value = 100
|
|
||||||
break
|
|
||||||
case "ambiance_add_finish":
|
|
||||||
var title = document.querySelector("#inputambiance > input[name='title']")
|
|
||||||
var url = document.querySelector("#inputambiance > input[name='url']")
|
|
||||||
var submit = document.querySelector("#inputambiance > input[name='submit']")
|
|
||||||
var progress = document.querySelector("#progressambiance progress")
|
|
||||||
|
|
||||||
title.value = ""
|
|
||||||
url.value = ""
|
|
||||||
|
|
||||||
title.disabled = false
|
|
||||||
url.disabled = false
|
|
||||||
submit.disabled = false
|
|
||||||
|
|
||||||
progress.value = 0
|
|
||||||
progress.style.display = "none"
|
|
||||||
break
|
|
||||||
case "ambiance_add":
|
|
||||||
const container = document.querySelector("#ambiance")
|
|
||||||
var newdiv = document.createElement('div');
|
|
||||||
newdiv.className = "item"
|
|
||||||
newdiv.dataset.id = data.payload.id
|
|
||||||
newdiv.innerText = data.payload.title
|
|
||||||
addInteractHandler(newdiv, (e, isTouch) => {
|
|
||||||
isTouch && e.preventDefault()
|
|
||||||
|
|
||||||
var id = e.target.dataset.id
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
"event": ((id === "reset") ? "ambiance_stop" : "ambiance_play"),
|
|
||||||
"payload": id
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
|
|
||||||
container.insertBefore(newdiv, document.querySelector("#ambiance div:last-child"))
|
|
||||||
var title = document.querySelector("#inputambiance > input[name='title']")
|
|
||||||
var url = document.querySelector("#inputambiance > input[name='url']")
|
|
||||||
var submit = document.querySelector("#inputambiance > input[name='submit']")
|
|
||||||
var progress = document.querySelector("#progressambiance progress")
|
|
||||||
|
|
||||||
title.value = ""
|
|
||||||
url.value = ""
|
|
||||||
|
|
||||||
title.disabled = false
|
|
||||||
url.disabled = false
|
|
||||||
submit.disabled = false
|
|
||||||
|
|
||||||
progress.value = 0
|
|
||||||
progress.style.display = "none"
|
|
||||||
|
|
||||||
break
|
|
||||||
case "ambiance_play":
|
case "ambiance_play":
|
||||||
document.querySelectorAll("#ambiance > div").forEach((e) => {e.style.removeProperty("background-color")})
|
document.querySelectorAll("#ambiance > div").forEach((e) => {e.style.removeProperty("background-color")})
|
||||||
document.querySelector(`#ambiance > div[data-id='${data.payload.id}']`).style.backgroundColor = "burlywood"
|
document.querySelector(`#ambiance > div[data-id='${data.payload.type}']`).style.backgroundColor = "burlywood"
|
||||||
ambiance.style.pointerEvents = 'auto'
|
ambiance.style.pointerEvents = 'auto'
|
||||||
break
|
break
|
||||||
case "ambiance_stop":
|
case "ambiance_stop":
|
||||||
|
@ -263,12 +157,11 @@ window.onload = function () {
|
||||||
addInteractHandler(document.querySelector("#addambiance"), (e, isTouch) => {
|
addInteractHandler(document.querySelector("#addambiance"), (e, isTouch) => {
|
||||||
isTouch && e.preventDefault()
|
isTouch && e.preventDefault()
|
||||||
|
|
||||||
//output.innerText = ""
|
output.innerText = ""
|
||||||
var title = document.querySelector("#inputambiance > input[name='title']")
|
var title = document.querySelector("#inputambiance > input[name='title']")
|
||||||
var url = document.querySelector("#inputambiance > input[name='url']")
|
var url = document.querySelector("#inputambiance > input[name='url']")
|
||||||
var submit = document.querySelector("#inputambiance > input[name='submit']")
|
|
||||||
if (title.value == "" || url.value == "") {
|
if (title.value == "" || url.value == "") {
|
||||||
console.log("Title or Url is empty!")
|
output.innerText = "Title or Url is empty!"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,16 +172,12 @@ window.onload = function () {
|
||||||
"url": url.value
|
"url": url.value
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
title.disabled = true
|
|
||||||
url.disabled = true
|
|
||||||
submit.disabled = true
|
|
||||||
})
|
})
|
||||||
|
|
||||||
addInteractHandler(submit, (e, isTouch) => {
|
addInteractHandler(submit, (e, isTouch) => {
|
||||||
isTouch && e.preventDefault()
|
isTouch && e.preventDefault()
|
||||||
|
|
||||||
//output.innerText = ""
|
output.innerText = ""
|
||||||
var title = document.querySelector("#inputplaylist > input[name='title']")
|
var title = document.querySelector("#inputplaylist > input[name='title']")
|
||||||
var url = document.querySelector("#inputplaylist > input[name='url']")
|
var url = document.querySelector("#inputplaylist > input[name='url']")
|
||||||
if (title.value == "" || url.value == "") {
|
if (title.value == "" || url.value == "") {
|
|
@ -1,6 +1,6 @@
|
||||||
package loop
|
package loop
|
||||||
|
|
||||||
import "github.com/gopxl/beep"
|
import "github.com/faiface/beep"
|
||||||
|
|
||||||
type loop struct {
|
type loop struct {
|
||||||
s beep.StreamSeekCloser
|
s beep.StreamSeekCloser
|
||||||
|
|
230
mpd.go
230
mpd.go
|
@ -1,230 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
discordspeaker "dndmusicbot/speaker"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
"syscall"
|
|
||||||
"text/template"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gopxl/beep"
|
|
||||||
"github.com/kataras/go-events"
|
|
||||||
"github.com/steino/gompd/v2/mpd"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MPD struct {
|
|
||||||
file int
|
|
||||||
f beep.Format
|
|
||||||
httpClient *http.Client
|
|
||||||
pcm *io.PipeReader
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
log.Println("mpd.go loading..")
|
|
||||||
|
|
||||||
f, err := os.Create(config.GetString("mpd.config"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t, err := template.New("mpd.tmpl").ParseFiles("tmpl/mpd.tmpl")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
mpd_conf := config.GetStringMapString("mpd")
|
|
||||||
|
|
||||||
err = t.Execute(f, mpd_conf)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Close()
|
|
||||||
|
|
||||||
pidstr, err := os.ReadFile(config.GetString("mpd.pid"))
|
|
||||||
if err != nil && os.IsNotExist(err) {
|
|
||||||
pid, _ := strconv.Atoi(string(bytes.TrimSpace(pidstr)))
|
|
||||||
proc, err := os.FindProcess(pid)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
log.Println(proc.Kill())
|
|
||||||
case os.ErrProcessDone:
|
|
||||||
log.Println("Pid alreadt finished, doing nothing.")
|
|
||||||
default:
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
} else if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
|
|
||||||
app.mpdc = cancel
|
|
||||||
|
|
||||||
cmd := exec.CommandContext(
|
|
||||||
ctx,
|
|
||||||
config.GetString("mpd.cmd"),
|
|
||||||
"--no-daemon",
|
|
||||||
config.GetString("mpd.config"),
|
|
||||||
"-v",
|
|
||||||
)
|
|
||||||
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
cancel()
|
|
||||||
}()
|
|
||||||
|
|
||||||
// wait for mpd to start.
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
|
|
||||||
app.mpd, err = mpd.Dial("unix", config.GetString("mpd.sock"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = app.mpd.NewPartition("ambiance")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.mpd.Repeat(true)
|
|
||||||
app.mpd.Random(true)
|
|
||||||
|
|
||||||
err = app.mpd.EnableOutput(0)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// To force the httpd stream to start.
|
|
||||||
err = app.mpd.Add(filepath.Join(cwd, "mp3/silence.mp3"))
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
app.mpd.Play(0)
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
app.mpd.Stop()
|
|
||||||
app.mpd.Clear()
|
|
||||||
|
|
||||||
err = app.mpd.Partition("ambiance")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.mpd.Repeat(true)
|
|
||||||
|
|
||||||
err = app.mpd.MoveOutput("ambiance")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// To force the httpd stream to start.
|
|
||||||
err = app.mpd.Add(filepath.Join(cwd, "mp3/silence.mp3"))
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
app.mpd.Play(0)
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
app.mpd.Stop()
|
|
||||||
app.mpd.Clear()
|
|
||||||
err = app.mpd.Partition("default")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.mpdw, err = mpd.NewWatcher("unix", config.GetString("mpd.sock"), "")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case ev := <-app.mpdw.Event:
|
|
||||||
app.events.Emit(events.EventName(ev))
|
|
||||||
case err := <-app.mpdw.Error:
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
log.Println("mpd.go done.")
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMPD(name string) (*MPD, error) {
|
|
||||||
out := new(MPD)
|
|
||||||
|
|
||||||
out.httpClient = &http.Client{}
|
|
||||||
|
|
||||||
var pcm *io.PipeWriter
|
|
||||||
out.pcm, pcm = io.Pipe()
|
|
||||||
|
|
||||||
f, err := syscall.Open(name, syscall.O_CREAT|syscall.O_RDONLY|syscall.O_CLOEXEC|syscall.O_NONBLOCK, 0644)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
buf := make([]byte, 2048)
|
|
||||||
for {
|
|
||||||
_, err := syscall.Read(f, buf)
|
|
||||||
if err != nil {
|
|
||||||
pcm.Write(make([]byte, 2048))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
pcm.Write(buf)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
out.f = beep.Format{
|
|
||||||
SampleRate: beep.SampleRate(sampleRate),
|
|
||||||
NumChannels: channels,
|
|
||||||
Precision: 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
out.file = f
|
|
||||||
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MPD) Err() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MPD) Stream(samples [][2]float64) (n int, ok bool) {
|
|
||||||
tmp := make([]byte, m.f.Width())
|
|
||||||
|
|
||||||
for i := range samples {
|
|
||||||
//dn, err := syscall.Read(m.file, tmp)
|
|
||||||
dn, err := m.pcm.Read(tmp)
|
|
||||||
if dn == len(tmp) {
|
|
||||||
samples[i], _ = m.f.DecodeSigned(tmp)
|
|
||||||
ok = true
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
samples[i] = discordspeaker.Silence
|
|
||||||
ok = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return len(samples), ok
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
package opus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/gopxl/beep"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
opusd "gopkg.in/hraban/opus.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
type decoder struct {
|
|
||||||
sync.Mutex
|
|
||||||
|
|
||||||
rc io.ReadSeekCloser
|
|
||||||
r *opusd.Stream
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(f io.ReadSeekCloser) (beep.StreamSeeker, error) {
|
|
||||||
d := new(decoder)
|
|
||||||
st, err := opusd.NewStream(f)
|
|
||||||
if err != nil {
|
|
||||||
return d, err
|
|
||||||
}
|
|
||||||
|
|
||||||
d.rc = f
|
|
||||||
d.r = st
|
|
||||||
|
|
||||||
return d, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *decoder) Err() error { return s.err }
|
|
||||||
|
|
||||||
func (d *decoder) Stream(samples [][2]float64) (n int, ok bool) {
|
|
||||||
if d.err != nil {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
var tmp [2]float32
|
|
||||||
for i := range samples {
|
|
||||||
dn, err := d.r.ReadFloat32(tmp[:])
|
|
||||||
|
|
||||||
if dn == 1 {
|
|
||||||
samples[i][0], samples[i][1] = float64(tmp[0]), float64(tmp[1])
|
|
||||||
n++
|
|
||||||
ok = true
|
|
||||||
}
|
|
||||||
if err == io.EOF {
|
|
||||||
ok = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
ok = false
|
|
||||||
d.err = errors.Wrap(err, "ogg/opus")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return n, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) Len() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) Position() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) Seek(p int) error {
|
|
||||||
_, err := d.rc.Seek(int64(p), io.SeekStart)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return errors.Wrap(err, "ogg/opus")
|
|
||||||
}
|
|
||||||
|
|
||||||
st, err := opusd.NewStream(d.rc)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return errors.Wrap(err, "ogg/opus")
|
|
||||||
}
|
|
||||||
|
|
||||||
d.r = st
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
package opus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gopxl/beep"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
mu sync.Mutex
|
|
||||||
mixer beep.Mixer
|
|
||||||
frameSize int = 960
|
|
||||||
samples = make([][2]float64, frameSize)
|
|
||||||
buf []byte
|
|
||||||
maxBytes int = (frameSize * 2) * 2
|
|
||||||
done chan struct{}
|
|
||||||
f beep.Format
|
|
||||||
)
|
|
||||||
|
|
||||||
func update() {
|
|
||||||
mu.Lock()
|
|
||||||
mixer.Stream(samples)
|
|
||||||
mu.Unlock()
|
|
||||||
|
|
||||||
for i := range samples {
|
|
||||||
for c := range samples[i] {
|
|
||||||
val := samples[i][c]
|
|
||||||
if val < -1 {
|
|
||||||
val = -1
|
|
||||||
}
|
|
||||||
if val > +1 {
|
|
||||||
val = +1
|
|
||||||
}
|
|
||||||
valInt16 := int16(val * (1<<15 - 1))
|
|
||||||
low := byte(valInt16)
|
|
||||||
high := byte(valInt16 >> 8)
|
|
||||||
buf[i*4+c*2+0] = low
|
|
||||||
buf[i*4+c*2+1] = high
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//log.Printf("%+v", buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMain(t *testing.T) {
|
|
||||||
buf = make([]byte, maxBytes)
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*9)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
file, err := os.Open("test.opus")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
d, err := New(file)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
f = beep.Format{
|
|
||||||
SampleRate: beep.SampleRate(48000),
|
|
||||||
NumChannels: 2,
|
|
||||||
Precision: 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
loop := beep.Loop(-1, d)
|
|
||||||
|
|
||||||
mu.Lock()
|
|
||||||
mixer.Add(loop)
|
|
||||||
mu.Unlock()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
default:
|
|
||||||
update()
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
BIN
opus/test.opus
BIN
opus/test.opus
Binary file not shown.
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
206
queue.go
206
queue.go
|
@ -1,55 +1,179 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"container/list"
|
||||||
"log"
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
"dndmusicbot/snapcast"
|
"github.com/faiface/beep"
|
||||||
|
"github.com/kataras/go-events"
|
||||||
"github.com/gopxl/beep"
|
|
||||||
"github.com/gopxl/beep/effects"
|
|
||||||
|
|
||||||
|
"dndmusicbot/ffmpeg"
|
||||||
discordspeaker "dndmusicbot/speaker"
|
discordspeaker "dndmusicbot/speaker"
|
||||||
)
|
)
|
||||||
|
|
||||||
var pl_volume *effects.Volume
|
type Ambiance struct {
|
||||||
var amb_volume *effects.Volume
|
Type string
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
log.Println("beep.go loading..")
|
app.ambiance = beep.Mixer{}
|
||||||
amb := beep.Mixer{}
|
discordspeaker.Play(&app.ambiance)
|
||||||
|
|
||||||
amb_volume = &effects.Volume{
|
app.queue = new(Queue)
|
||||||
Streamer: &amb,
|
app.queue.list = list.New()
|
||||||
Base: 2,
|
app.queue.Events = app.events
|
||||||
Volume: -2,
|
discordspeaker.Play(app.queue)
|
||||||
Silent: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
discordspeaker.Play(amb_volume)
|
log.Println("queue.go done.")
|
||||||
|
}
|
||||||
amb_stream, err := snapcast.New("127.0.0.1", config.GetInt("mpd.ambiance"))
|
|
||||||
if err != nil {
|
type Song struct {
|
||||||
log.Fatal(err)
|
Title string
|
||||||
}
|
Channel string
|
||||||
amb.Add(amb_stream)
|
VideoID string
|
||||||
|
Length time.Duration
|
||||||
pl := beep.Mixer{}
|
PCM *ffmpeg.PCM
|
||||||
|
Playlist Playlist
|
||||||
pl_volume = &effects.Volume{
|
DLuri string
|
||||||
Streamer: &pl,
|
}
|
||||||
Base: 2,
|
|
||||||
Volume: -2,
|
func (s *Song) NewStream() (err error) {
|
||||||
Silent: false,
|
s.PCM, err = ffmpeg.NewPCM(s.DLuri, sampleRate, channels)
|
||||||
}
|
return
|
||||||
|
}
|
||||||
discordspeaker.Play(pl_volume)
|
|
||||||
|
type Queue struct {
|
||||||
pl_stream, err := snapcast.New("127.0.0.1", config.GetInt("mpd.playlist"))
|
Events events.EventEmmiter
|
||||||
if err != nil {
|
playing bool
|
||||||
log.Fatal(err)
|
list *list.List
|
||||||
}
|
current *list.Element
|
||||||
|
}
|
||||||
pl.Add(pl_stream)
|
|
||||||
|
func (q Queue) IsPlaying() bool {
|
||||||
log.Println("beep.go done.")
|
return q.playing
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue) Play() {
|
||||||
|
q.playing = true
|
||||||
|
if q.list.Len() > 0 {
|
||||||
|
play := q.list.Front()
|
||||||
|
q.current = play
|
||||||
|
app.events.Emit("song_start")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue) Add(s *Song) {
|
||||||
|
el := q.list.PushBack(s)
|
||||||
|
if el == q.list.Front() {
|
||||||
|
q.Play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q Queue) QLen() int {
|
||||||
|
return q.list.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q Queue) Current() *Song {
|
||||||
|
if q.current == nil {
|
||||||
|
return new(Song)
|
||||||
|
}
|
||||||
|
return q.current.Value.(*Song)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue) Reset() {
|
||||||
|
q.playing = false
|
||||||
|
|
||||||
|
q.current = nil
|
||||||
|
q.list = q.list.Init()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue) Next() {
|
||||||
|
if q.current == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next := q.current.Next()
|
||||||
|
if next == nil {
|
||||||
|
next = q.list.Front()
|
||||||
|
}
|
||||||
|
|
||||||
|
pcm := next.Value.(*Song).PCM
|
||||||
|
if pcm != nil && pcm.Position() != 0 {
|
||||||
|
err := next.Value.(*Song).NewStream()
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
q.current = next
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue) Prev() {
|
||||||
|
if q.current == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
prev := q.current.Prev()
|
||||||
|
if prev == nil {
|
||||||
|
prev = q.list.Back()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := prev.Value.(*Song).NewStream()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
q.current = prev
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue) Preload() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue) Stream(samples [][2]float64) (n int, ok bool) {
|
||||||
|
// We use the filled variable to track how many samples we've
|
||||||
|
// successfully filled already. We loop until all samples are filled.
|
||||||
|
filled := 0
|
||||||
|
|
||||||
|
for filled < len(samples) {
|
||||||
|
// There are no streamers in the queue, so we stream silence.
|
||||||
|
if q.current == nil || q.list.Len() == 0 {
|
||||||
|
for i := range samples[filled:] {
|
||||||
|
samples[i][0] = 0
|
||||||
|
samples[i][1] = 0
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// We stream from the first streamer in the queue.
|
||||||
|
n, ok := q.current.Value.(*Song).PCM.Stream(samples[filled:])
|
||||||
|
// If it's drained, we pop it from the queue, thus continuing with
|
||||||
|
// the next streamer.
|
||||||
|
if !ok {
|
||||||
|
q.Next()
|
||||||
|
q.Events.Emit("song_over", nil)
|
||||||
|
}
|
||||||
|
// We update the number of filled samples.
|
||||||
|
filled += n
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(samples), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue) Err() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue) Position() int {
|
||||||
|
if q.current == nil || q.current.Value.(*Song).PCM == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return q.current.Value.(*Song).PCM.Position()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q Queue) Len() int {
|
||||||
|
if q.current == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(q.current.Value.(*Song).Length.Milliseconds())
|
||||||
}
|
}
|
||||||
|
|
208
routes.go
208
routes.go
|
@ -1,65 +1,31 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/bitly/go-simplejson"
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/gorilla/sessions"
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
"github.com/markbates/goth"
|
|
||||||
"github.com/markbates/goth/gothic"
|
|
||||||
"github.com/markbates/goth/providers/discord"
|
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const COOKIE_NAME = "_dndmusicbot"
|
|
||||||
|
|
||||||
var upgrader = websocket.Upgrader{
|
var upgrader = websocket.Upgrader{
|
||||||
ReadBufferSize: 1024,
|
ReadBufferSize: 1024,
|
||||||
WriteBufferSize: 1024,
|
WriteBufferSize: 1024,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
key := []byte(os.Getenv("SESSION_SECRET"))
|
|
||||||
maxAge := 86400 * 30 // 30 days
|
|
||||||
isProd := true // Set to true when serving over https
|
|
||||||
|
|
||||||
store := sessions.NewCookieStore([]byte(key))
|
|
||||||
store.MaxAge(maxAge)
|
|
||||||
store.Options.Path = "/"
|
|
||||||
store.Options.HttpOnly = true // HttpOnly should always be enabled
|
|
||||||
store.Options.Secure = isProd
|
|
||||||
|
|
||||||
gothic.Store = store
|
|
||||||
goth.UseProviders(
|
|
||||||
discord.New(config.GetString("discord.id"), config.GetString("discord.secret"), config.GetString("discord.callback"), discord.ScopeIdentify, discord.ScopeEmail, discord.ScopeGuilds, discord.ScopeReadGuilds),
|
|
||||||
)
|
|
||||||
|
|
||||||
app.router = httprouter.New()
|
app.router = httprouter.New()
|
||||||
|
|
||||||
app.router.GET("/", auth(app.Index))
|
app.router.GET("/", app.Index)
|
||||||
app.router.GET("/playlists", auth(app.Web_Playlists))
|
app.router.GET("/play/:playlist", app.Play)
|
||||||
app.router.GET("/ambiance", auth(app.Web_Ambiance))
|
app.router.GET("/reset", app.Reset)
|
||||||
app.router.GET("/play/:playlist", auth(app.Play))
|
app.router.GET("/public/*js", app.ServeFiles)
|
||||||
app.router.GET("/reset", auth(app.Reset))
|
app.router.GET("/css/*css", app.ServeFiles)
|
||||||
app.router.GET("/public/*js", auth(app.ServeFiles))
|
|
||||||
app.router.GET("/css/*css", auth(app.ServeFiles))
|
|
||||||
app.router.GET("/auth/callback", app.AuthHandler)
|
|
||||||
app.router.GET("/youtube/:id", local(ProxyTube))
|
|
||||||
|
|
||||||
app.router.HandlerFunc("GET", "/ws", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
app.router.HandlerFunc("GET", "/ws", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Printf("WS connection from %v\n", r.RemoteAddr)
|
log.Printf("WS connection from %v\n", r.RemoteAddr)
|
||||||
|
@ -69,152 +35,20 @@ func init() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ws.join(conn)
|
err = handleWS(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("WS connection closed, %v\n", r.RemoteAddr)
|
log.Printf("WS connection closed, %v\n", r.RemoteAddr)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
log.Fatal(http.ListenAndServe(":"+config.GetString("web.port"), app.router))
|
log.Fatal(http.ListenAndServe(":8824", app.router))
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
type IndexData struct {
|
type IndexData struct {
|
||||||
Playlists []Playlist
|
Playlists []Playlist
|
||||||
Ambiance []Ambiance
|
Ambiance []string
|
||||||
}
|
|
||||||
|
|
||||||
func (app App) AuthHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
|
||||||
user, err := gothic.CompleteUserAuth(w, r)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
profile_url := &url.URL{Scheme: "https",
|
|
||||||
Host: "discord.com",
|
|
||||||
Path: "/api/users/@me",
|
|
||||||
}
|
|
||||||
member_url := fmt.Sprintf("%s/guilds/%s/member", profile_url.String(), config.GetString("discord.guild"))
|
|
||||||
member_req, err := http.NewRequest("GET", member_url, nil)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
member_req.Header.Add("Authorization", "Bearer "+user.AccessToken)
|
|
||||||
member_resp, err := http.DefaultClient.Do(member_req)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
member_body, _ := io.ReadAll(member_resp.Body)
|
|
||||||
defer member_resp.Body.Close()
|
|
||||||
|
|
||||||
member, err := simplejson.NewJson(member_body)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
groups, err := member.GetPath("roles").StringArray()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ok := false
|
|
||||||
|
|
||||||
for _, group := range config.GetStringSlice("discord.groups") {
|
|
||||||
if slices.Contains(groups, group) {
|
|
||||||
ok = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok {
|
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
|
||||||
"nbf": time.Now().Unix(),
|
|
||||||
"exp": time.Now().Add(time.Hour * 720).Unix(),
|
|
||||||
})
|
|
||||||
|
|
||||||
tokenString, err := token.SignedString([]byte(os.Getenv("SESSION_SECRET")))
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cookie := new(http.Cookie)
|
|
||||||
cookie.Name = COOKIE_NAME
|
|
||||||
cookie.Value = tokenString
|
|
||||||
cookie.Path = "/"
|
|
||||||
cookie.Secure = true
|
|
||||||
cookie.HttpOnly = true
|
|
||||||
cookie.MaxAge = 86400 * 30
|
|
||||||
|
|
||||||
http.SetCookie(w, cookie)
|
|
||||||
http.RedirectHandler("/", http.StatusFound).ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Middleware to check that the connection is local.
|
|
||||||
func local(n httprouter.Handle) httprouter.Handle {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
|
||||||
addr, err := netip.ParseAddrPort(r.RemoteAddr)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case addr.Addr().IsLoopback():
|
|
||||||
fallthrough
|
|
||||||
case addr.Addr().IsPrivate():
|
|
||||||
n(w, r, ps)
|
|
||||||
default:
|
|
||||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func auth(n httprouter.Handle) httprouter.Handle {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
|
||||||
if os.Getenv("APP_ENV") == "test" {
|
|
||||||
n(w, r, ps)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
values := r.URL.Query()
|
|
||||||
values.Add("provider", "discord")
|
|
||||||
r.URL.RawQuery = values.Encode()
|
|
||||||
|
|
||||||
auth_cookie, err := r.Cookie(COOKIE_NAME)
|
|
||||||
if err == nil {
|
|
||||||
token, err := jwt.Parse(auth_cookie.Value, func(token *jwt.Token) (interface{}, error) {
|
|
||||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
|
||||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
|
||||||
}
|
|
||||||
|
|
||||||
return []byte(os.Getenv("SESSION_SECRET")), nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if token.Valid {
|
|
||||||
n(w, r, ps)
|
|
||||||
} else {
|
|
||||||
gothic.BeginAuthHandler(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if err == http.ErrNoCookie {
|
|
||||||
gothic.BeginAuthHandler(w, r)
|
|
||||||
return
|
|
||||||
} else if err != nil {
|
|
||||||
fmt.Fprintln(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app App) ServeFiles(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
func (app App) ServeFiles(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||||
|
@ -245,7 +79,7 @@ func (app App) Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params
|
||||||
http.Error(w, "Unable to get playlists. "+err.Error(), http.StatusInternalServerError)
|
http.Error(w, "Unable to get playlists. "+err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
amblist, err := GetAmbiances()
|
amblist, err := GetAmbiance()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return
|
return
|
||||||
|
@ -260,30 +94,6 @@ func (app App) Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app App) Web_Playlists(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
|
||||||
playlists, err := app.GetPlaylists()
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Unable to get playlists. "+err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.NewEncoder(w).Encode(playlists)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Unable to get playlists. "+err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app App) Web_Ambiance(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
|
||||||
ambiance, err := GetAmbiances()
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Unable to get ambiance. "+err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.NewEncoder(w).Encode(ambiance)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Unable to get playlists. "+err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *App) Play(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
func (app *App) Play(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||||
plname := p.ByName("playlist")
|
plname := p.ByName("playlist")
|
||||||
|
|
||||||
|
|
|
@ -1,79 +0,0 @@
|
||||||
package snapcast
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/gopxl/beep"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Stream struct {
|
|
||||||
sync.Mutex
|
|
||||||
|
|
||||||
Out io.ReadCloser
|
|
||||||
Cancel context.CancelFunc
|
|
||||||
Cmd *exec.Cmd
|
|
||||||
|
|
||||||
f beep.Format
|
|
||||||
buf []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(host string, port int) (ff beep.Streamer, err error) {
|
|
||||||
st := new(Stream)
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
st.Cancel = cancel
|
|
||||||
|
|
||||||
st.Cmd = exec.CommandContext(
|
|
||||||
ctx,
|
|
||||||
"/usr/bin/snapclient",
|
|
||||||
"-h", host,
|
|
||||||
"-p", strconv.Itoa(port),
|
|
||||||
"--player", "file:filename=stderr",
|
|
||||||
"--logfilter", "*:error",
|
|
||||||
)
|
|
||||||
|
|
||||||
st.Cmd.Stdout = os.Stdin
|
|
||||||
|
|
||||||
st.Out, err = st.Cmd.StderrPipe()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = st.Cmd.Start()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
st.buf = make([]byte, 4)
|
|
||||||
st.f = beep.Format{
|
|
||||||
SampleRate: beep.SampleRate(48000),
|
|
||||||
NumChannels: 2,
|
|
||||||
Precision: 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
return st, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Stream) Stream(samples [][2]float64) (n int, ok bool) {
|
|
||||||
for i := range samples {
|
|
||||||
_, err := d.Out.Read(d.buf)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
ok = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
samples[i], _ = d.f.DecodeSigned(d.buf)
|
|
||||||
ok = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return len(samples), ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Stream) Err() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,61 +1,52 @@
|
||||||
package discordspeaker
|
package discordspeaker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"bytes"
|
||||||
"io"
|
"encoding/binary"
|
||||||
"log"
|
"log"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/diamondburned/arikawa/v3/voice"
|
"github.com/bwmarrin/discordgo"
|
||||||
"github.com/diamondburned/arikawa/v3/voice/voicegateway"
|
"github.com/faiface/beep"
|
||||||
"github.com/gopxl/beep"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/time/rate"
|
"layeh.com/gopus"
|
||||||
"gopkg.in/hraban/opus.v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const bufferSize = 1000
|
||||||
|
|
||||||
var (
|
var (
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
mixer beep.Mixer
|
mixer beep.Mixer
|
||||||
samples [][2]float64
|
samples [][2]float64
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
encoder *opus.Encoder
|
encoder *gopus.Encoder
|
||||||
|
voice *discordgo.VoiceConnection
|
||||||
frameSize int = 960
|
frameSize int = 960
|
||||||
channels int = 2
|
channels int = 2
|
||||||
sampleRate int = 48000
|
sampleRate int = 48000
|
||||||
maxBytes int = (frameSize * 2) * 2
|
maxBytes int = (frameSize * 2) * 2
|
||||||
|
pcm []int16
|
||||||
buf []byte
|
buf []byte
|
||||||
session *voice.Session
|
pause bool
|
||||||
pw *io.PipeWriter
|
|
||||||
pr *io.PipeReader
|
|
||||||
|
|
||||||
spklimit = rate.NewLimiter(rate.Every(5*time.Second), 1)
|
|
||||||
spk = true
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var start time.Time
|
func Init(dgv *discordgo.VoiceConnection) error {
|
||||||
|
|
||||||
var Silence = [2]float64{}
|
|
||||||
|
|
||||||
var dmutex = sync.Mutex{}
|
|
||||||
|
|
||||||
func Init(dgv *voice.Session, bitrate int) error {
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
Close()
|
Close()
|
||||||
|
|
||||||
pr, pw = io.Pipe()
|
|
||||||
buf = make([]byte, maxBytes)
|
|
||||||
mixer = beep.Mixer{}
|
mixer = beep.Mixer{}
|
||||||
|
|
||||||
|
buf = make([]byte, maxBytes)
|
||||||
|
pcm = make([]int16, frameSize*channels)
|
||||||
samples = make([][2]float64, frameSize)
|
samples = make([][2]float64, frameSize)
|
||||||
|
|
||||||
session = dgv
|
pause = true
|
||||||
|
|
||||||
encoder, err = opus.NewEncoder(sampleRate, channels, opus.AppAudio)
|
voice = dgv
|
||||||
encoder.SetBitrate(bitrate)
|
|
||||||
|
encoder, err = gopus.NewEncoder(sampleRate, channels, gopus.Audio)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to initialize speaker")
|
return errors.Wrap(err, "failed to initialize speaker")
|
||||||
|
@ -72,16 +63,6 @@ func Init(dgv *voice.Session, bitrate int) error {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
|
||||||
dmutex.Lock()
|
|
||||||
_, err := io.Copy(session, pr)
|
|
||||||
dmutex.Unlock()
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,74 +90,46 @@ func Clear() {
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Speak(s bool) {
|
func Pause(p bool) {
|
||||||
switch s {
|
pause = p
|
||||||
case true:
|
|
||||||
|
|
||||||
case false:
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckSilence(samples [][2]float64) {
|
func IsPaused() bool {
|
||||||
if IsSilent(samples) {
|
return pause
|
||||||
if spk && spklimit.Allow() {
|
|
||||||
log.Println("Notspeaking")
|
|
||||||
session.Speaking(context.Background(), voicegateway.NotSpeaking)
|
|
||||||
spk = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !spk {
|
|
||||||
log.Println("Speaking")
|
|
||||||
session.Speaking(context.Background(), voicegateway.Microphone)
|
|
||||||
spk = true
|
|
||||||
spklimit.Reserve()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func update() {
|
func update() {
|
||||||
start = time.Now()
|
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
mixer.Stream(samples)
|
mixer.Stream(samples)
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
|
|
||||||
go CheckSilence(samples)
|
for i := range samples {
|
||||||
|
for c := range samples[i] {
|
||||||
if !spk {
|
val := samples[i][c]
|
||||||
return
|
if val < -1 {
|
||||||
}
|
val = -1
|
||||||
|
}
|
||||||
f32 := make([]float32, len(samples)*2)
|
if val > +1 {
|
||||||
var idx int
|
val = +1
|
||||||
|
}
|
||||||
for i := 0; i < len(samples); i++ {
|
valInt16 := int16(val * (1<<15 - 1))
|
||||||
f32[idx] = float32(samples[i][0])
|
low := byte(valInt16)
|
||||||
idx++
|
high := byte(valInt16 >> 8)
|
||||||
f32[idx] = float32(samples[i][1])
|
buf[i*4+c*2+0] = low
|
||||||
idx++
|
buf[i*4+c*2+1] = high
|
||||||
}
|
|
||||||
n, err := encoder.EncodeFloat32(f32, buf)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = pw.Write(buf[:n])
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//fmt.Println(time.Since(start), len(samples), n)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsSilent(in [][2]float64) bool {
|
|
||||||
for _, v := range in {
|
|
||||||
if v != Silence {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
|
binary.Read(bytes.NewReader(buf), binary.LittleEndian, &pcm)
|
||||||
|
opus, err := encoder.Encode(pcm, frameSize, maxBytes)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if voice.Ready == false || voice.OpusSend == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
voice.OpusSend <- opus
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,188 +0,0 @@
|
||||||
import React, { useEffect, useState } from "react";
|
|
||||||
import CSS from "csstype";
|
|
||||||
import { on } from "./events";
|
|
||||||
import ws from "./ws";
|
|
||||||
import SortableList, { SortableItem } from "react-easy-sort";
|
|
||||||
import { arrayMoveImmutable } from "array-move";
|
|
||||||
|
|
||||||
export default function Ambiance() {
|
|
||||||
interface Ambiance {
|
|
||||||
Id: string;
|
|
||||||
Title: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [ambiance, setAmbiance] = useState<Ambiance[]>([]);
|
|
||||||
const [active, setActive] = useState("");
|
|
||||||
const [title, setTitle] = useState("");
|
|
||||||
const [url, setUrl] = useState("");
|
|
||||||
const [percent, setPercent] = useState(0);
|
|
||||||
const [running, setRunning] = useState(false);
|
|
||||||
|
|
||||||
const onSortEnd = (oldIndex: number, newIndex: number) => {
|
|
||||||
setAmbiance((array) => arrayMoveImmutable(array, oldIndex, newIndex));
|
|
||||||
};
|
|
||||||
|
|
||||||
const activeStyle: CSS.Properties = {
|
|
||||||
backgroundColor: "burlywood",
|
|
||||||
};
|
|
||||||
|
|
||||||
const getOrder = (name: string) => {
|
|
||||||
var order = localStorage.getItem("dndmusicbot-" + name);
|
|
||||||
return order ? order.split("|") : [];
|
|
||||||
};
|
|
||||||
|
|
||||||
const setOrder = (name: string) => {
|
|
||||||
var order = ambiance.map((a) => a.Id);
|
|
||||||
localStorage.setItem("dndmusicbot-" + name, order.join("|"));
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchAmbiance = () => {
|
|
||||||
const order = getOrder("ambiance");
|
|
||||||
|
|
||||||
fetch("/ambiance")
|
|
||||||
.then((response) => {
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then((data) => {
|
|
||||||
data.sort(
|
|
||||||
(a: Ambiance, b: Ambiance) =>
|
|
||||||
order.indexOf(a.Id) - order.indexOf(b.Id)
|
|
||||||
);
|
|
||||||
setAmbiance(data);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchAmbiance();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setOrder("ambiance");
|
|
||||||
}, [ambiance]);
|
|
||||||
|
|
||||||
const Play = (e: any) => {
|
|
||||||
ws.send(
|
|
||||||
JSON.stringify({
|
|
||||||
event: "ambiance_play",
|
|
||||||
payload: e.target.dataset.id,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Stop = () => {
|
|
||||||
ws.send(
|
|
||||||
JSON.stringify({
|
|
||||||
event: "ambiance_stop",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const AddAmbiance = () => {
|
|
||||||
if (title == "" || url == "") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ws.send(
|
|
||||||
JSON.stringify({
|
|
||||||
event: "ambiance_add",
|
|
||||||
payload: {
|
|
||||||
title: title,
|
|
||||||
url: url,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
on("dnd:ambiance_add", () => fetchAmbiance());
|
|
||||||
on("dnd:ambiance_add_start", () => setRunning(true));
|
|
||||||
on("dnd:ambiance_add_finish", () => {
|
|
||||||
setRunning(false);
|
|
||||||
setPercent(0);
|
|
||||||
setTitle("");
|
|
||||||
setUrl("");
|
|
||||||
});
|
|
||||||
on("dnd:ambiance_play", (e: any) => setActive(e.detail.id));
|
|
||||||
on("dnd:ambiance_stop", () => setActive(""));
|
|
||||||
on("dnd:ambiance_encode_finish", () => setPercent(0));
|
|
||||||
on("dnd:ambiance_encode_progress", (e: any) => {
|
|
||||||
setRunning(true);
|
|
||||||
|
|
||||||
const p = parseInt(e.detail.percent, 10);
|
|
||||||
if (!Number.isNaN(p)) {
|
|
||||||
setPercent(p);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<h2 className="bot">Ambiance</h2>
|
|
||||||
<section>
|
|
||||||
<SortableList
|
|
||||||
onSortEnd={onSortEnd}
|
|
||||||
className="item-container"
|
|
||||||
draggedItemClassName="dragged"
|
|
||||||
>
|
|
||||||
{ambiance.map((item) => {
|
|
||||||
return (
|
|
||||||
<SortableItem key={item.Id}>
|
|
||||||
<div
|
|
||||||
className="item drag"
|
|
||||||
data-id={item.Id}
|
|
||||||
style={item.Id == active ? activeStyle : {}}
|
|
||||||
onClick={Play}
|
|
||||||
>
|
|
||||||
{item.Title}
|
|
||||||
</div>
|
|
||||||
</SortableItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
<div className="item locked stop" data-id="reset" onClick={Stop}>
|
|
||||||
Stop
|
|
||||||
</div>
|
|
||||||
</SortableList>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<div id="progressambiance" className="input-container">
|
|
||||||
<progress
|
|
||||||
max="100"
|
|
||||||
value={percent}
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
className={running ? "" : "u-hidden"}
|
|
||||||
>
|
|
||||||
{percent}%
|
|
||||||
</progress>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section className={running ? "u-hidden" : ""}>
|
|
||||||
<div id="inputambiance" className="input-container">
|
|
||||||
<input
|
|
||||||
className="u-full-width"
|
|
||||||
name="title"
|
|
||||||
type="text"
|
|
||||||
value={title}
|
|
||||||
placeholder="Enter name.."
|
|
||||||
onChange={(e) => setTitle(e.target.value)}
|
|
||||||
disabled={running}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
className="u-full-width"
|
|
||||||
name="url"
|
|
||||||
type="text"
|
|
||||||
value={url}
|
|
||||||
placeholder="Enter url..."
|
|
||||||
onChange={(e) => setUrl(e.target.value)}
|
|
||||||
disabled={running}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
id="addambiance"
|
|
||||||
name="submit"
|
|
||||||
value="Add"
|
|
||||||
type="submit"
|
|
||||||
onClick={AddAmbiance}
|
|
||||||
disabled={running}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
import React, { useState } from "react";
|
|
||||||
import CSS from "csstype";
|
|
||||||
import { on } from "./events";
|
|
||||||
import ws from "./ws";
|
|
||||||
|
|
||||||
export default function Controls() {
|
|
||||||
const [channel, setChannel] = useState("");
|
|
||||||
const [title, setTitle] = useState("");
|
|
||||||
const [pos, setPos] = useState(0);
|
|
||||||
const [len, setLen] = useState(0);
|
|
||||||
const [song, setSong] = useState("");
|
|
||||||
const [pause, setPause] = useState(true);
|
|
||||||
|
|
||||||
const linkStyle: CSS.Properties = {
|
|
||||||
textDecoration: "none",
|
|
||||||
};
|
|
||||||
|
|
||||||
const Next = () => {
|
|
||||||
ws.send(
|
|
||||||
JSON.stringify({
|
|
||||||
event: "next",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Prev = () => {
|
|
||||||
ws.send(
|
|
||||||
JSON.stringify({
|
|
||||||
event: "next",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const msToTime = (duration: number) => {
|
|
||||||
var milliseconds = (duration % 1000) / 100,
|
|
||||||
seconds = Math.floor((duration / 1000) % 60),
|
|
||||||
minutes = Math.floor((duration / (1000 * 60)) % 60),
|
|
||||||
minutes = minutes < 10 ? 0 + minutes : minutes;
|
|
||||||
seconds = seconds < 10 ? 0 + seconds : seconds;
|
|
||||||
|
|
||||||
return (
|
|
||||||
minutes.toString().padStart(2, "0") +
|
|
||||||
":" +
|
|
||||||
seconds.toString().padStart(2, "0")
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
on("dnd:song_info", (e: any) => {
|
|
||||||
setChannel(e.detail.channel);
|
|
||||||
setTitle(e.detail.current);
|
|
||||||
setPos(e.detail.position);
|
|
||||||
setLen(e.detail.len);
|
|
||||||
setSong("https://youtu.be/" + e.detail.song);
|
|
||||||
setPause(e.detail.pause);
|
|
||||||
});
|
|
||||||
|
|
||||||
on("dnd:song_position", (e: any) => {
|
|
||||||
setPos(e.detail.position);
|
|
||||||
setLen(e.detail.len);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section>
|
|
||||||
<div id="info">
|
|
||||||
<a
|
|
||||||
href={song}
|
|
||||||
id="link"
|
|
||||||
style={linkStyle}
|
|
||||||
className={pause ? "u-hidden" : ""}
|
|
||||||
>
|
|
||||||
<span id="channel">{channel}</span>
|
|
||||||
<span> - </span>
|
|
||||||
<span id="title">{title}</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<div className={pause ? "controls u-hidden" : "controls"}>
|
|
||||||
<input
|
|
||||||
id="prev"
|
|
||||||
name="prev"
|
|
||||||
type="button"
|
|
||||||
value="prev"
|
|
||||||
onClick={Prev}
|
|
||||||
/>
|
|
||||||
<span id="time">
|
|
||||||
{msToTime(pos)} / {msToTime(len)}
|
|
||||||
</span>
|
|
||||||
<input
|
|
||||||
id="next"
|
|
||||||
name="next"
|
|
||||||
type="button"
|
|
||||||
value="next"
|
|
||||||
onClick={Next}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
function on(eventType: any, listener: { (event: any): void; (this: Document, ev: any): any; }) {
|
|
||||||
document.addEventListener(eventType, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
function off(eventType: any, listener: { (event: any): void; (this: Document, ev: any): any; }) {
|
|
||||||
document.removeEventListener(eventType, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
function once(eventType: any, listener: (arg0: any) => void) {
|
|
||||||
on(eventType, handleEventOnce);
|
|
||||||
|
|
||||||
function handleEventOnce(event: any) {
|
|
||||||
listener(event);
|
|
||||||
off(eventType, handleEventOnce);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function trigger(eventType: string, data: any) {
|
|
||||||
const event = new CustomEvent(eventType, { detail: data });
|
|
||||||
document.dispatchEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
export { on, once, off, trigger };
|
|
|
@ -1,17 +0,0 @@
|
||||||
import { useState, useEffect } from "react";
|
|
||||||
|
|
||||||
export const useDebounce = (value: any, milliSeconds: number) => {
|
|
||||||
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handler = setTimeout(() => {
|
|
||||||
setDebouncedValue(value);
|
|
||||||
}, milliSeconds);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
clearTimeout(handler);
|
|
||||||
};
|
|
||||||
}, [value, milliSeconds]);
|
|
||||||
|
|
||||||
return debouncedValue;
|
|
||||||
};
|
|
|
@ -1,26 +0,0 @@
|
||||||
import React, { useState } from "react";
|
|
||||||
import ws from "./ws";
|
|
||||||
|
|
||||||
export default function Modal() {
|
|
||||||
const [waiting, setWaiting] = useState(true);
|
|
||||||
|
|
||||||
ws.onopen = () => {
|
|
||||||
console.log("false");
|
|
||||||
setWaiting(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onclose = () => {
|
|
||||||
console.log("true");
|
|
||||||
setWaiting(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div id="waiting" className={waiting ? "modal" : "modal u-hidden"}>
|
|
||||||
<div className="modal-content">
|
|
||||||
<p>Waiting for bot..</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,163 +0,0 @@
|
||||||
import React, { useEffect, useState } from "react";
|
|
||||||
import CSS from "csstype";
|
|
||||||
import { on } from "./events";
|
|
||||||
import ws from "./ws";
|
|
||||||
import SortableList, { SortableItem } from "react-easy-sort";
|
|
||||||
import { arrayMoveImmutable } from "array-move";
|
|
||||||
|
|
||||||
export default function Playlists() {
|
|
||||||
interface Playlist {
|
|
||||||
Id: string;
|
|
||||||
Title: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [playlists, setPlaylists] = useState<Playlist[]>([]);
|
|
||||||
const [active, setActive] = useState("");
|
|
||||||
const [title, setTitle] = useState("");
|
|
||||||
const [url, setUrl] = useState("");
|
|
||||||
const [output, setOutput] = useState("");
|
|
||||||
|
|
||||||
const onSortEnd = (oldIndex: number, newIndex: number) => {
|
|
||||||
setPlaylists((array) => arrayMoveImmutable(array, oldIndex, newIndex));
|
|
||||||
};
|
|
||||||
|
|
||||||
const activeStyle: CSS.Properties = {
|
|
||||||
backgroundColor: "burlywood",
|
|
||||||
};
|
|
||||||
|
|
||||||
const getOrder = (name: string) => {
|
|
||||||
var order = localStorage.getItem("dndmusicbot-" + name);
|
|
||||||
return order ? order.split("|") : [];
|
|
||||||
};
|
|
||||||
|
|
||||||
const setOrder = (name: string) => {
|
|
||||||
var order = playlists.map((a) => a.Id);
|
|
||||||
localStorage.setItem("dndmusicbot-" + name, order.join("|"));
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchPlaylists = () => {
|
|
||||||
const order = getOrder("playlist");
|
|
||||||
|
|
||||||
fetch("/playlists")
|
|
||||||
.then((response) => {
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then((data) => {
|
|
||||||
data.sort(
|
|
||||||
(a: Playlist, b: Playlist) =>
|
|
||||||
order.indexOf(a.Id) - order.indexOf(b.Id)
|
|
||||||
);
|
|
||||||
setPlaylists(data);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchPlaylists();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setOrder("playlist");
|
|
||||||
}, [playlists]);
|
|
||||||
|
|
||||||
const Play = (e: any) => {
|
|
||||||
ws.send(
|
|
||||||
JSON.stringify({
|
|
||||||
event: "load_playlist",
|
|
||||||
payload: e.target.dataset.id,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Stop = () => {
|
|
||||||
ws.send(
|
|
||||||
JSON.stringify({
|
|
||||||
event: "stop",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const AddPlaylist = () => {
|
|
||||||
if (title == "" || url == "") {
|
|
||||||
setOutput("Title or Url is empty!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ws.send(
|
|
||||||
JSON.stringify({
|
|
||||||
event: "add_playlist",
|
|
||||||
payload: {
|
|
||||||
title: title,
|
|
||||||
url: url,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
on("dnd:song_info", (e: any) => {
|
|
||||||
setActive(e.detail.playlist);
|
|
||||||
});
|
|
||||||
|
|
||||||
on("dnd:new_playlist", (e: any) => {
|
|
||||||
fetchPlaylists();
|
|
||||||
setOutput("New playlist was added: " + e.details.title);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<h2 className="bot">Playlists</h2>
|
|
||||||
<section>
|
|
||||||
<SortableList
|
|
||||||
onSortEnd={onSortEnd}
|
|
||||||
className="item-container"
|
|
||||||
draggedItemClassName="dragged"
|
|
||||||
>
|
|
||||||
{playlists.map((playlist) => {
|
|
||||||
return (
|
|
||||||
<SortableItem key={playlist.Id}>
|
|
||||||
<div
|
|
||||||
onClick={Play}
|
|
||||||
className="item"
|
|
||||||
data-id={playlist.Id}
|
|
||||||
style={playlist.Id == active ? activeStyle : {}}
|
|
||||||
>
|
|
||||||
{playlist.Title}
|
|
||||||
</div>
|
|
||||||
</SortableItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
<div className="item locked stop" onClick={Stop} data-id="reset">
|
|
||||||
Stop
|
|
||||||
</div>
|
|
||||||
</SortableList>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<div id="inputplaylist" className="input-container">
|
|
||||||
<input
|
|
||||||
className="u-full-width"
|
|
||||||
name="title"
|
|
||||||
type="text"
|
|
||||||
placeholder="Enter name.."
|
|
||||||
onChange={(e) => setTitle(e.target.value)}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
className="u-full-width"
|
|
||||||
name="url"
|
|
||||||
type="text"
|
|
||||||
placeholder="https://youtube.com/playlist?list=..."
|
|
||||||
onChange={(e) => setUrl(e.target.value)}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
id="addplaylist"
|
|
||||||
name="submit"
|
|
||||||
value="Add"
|
|
||||||
type="submit"
|
|
||||||
onClick={AddPlaylist}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<p>{output}</p>
|
|
||||||
</section>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
import React, { useState } from "react";
|
|
||||||
import { createRoot } from "react-dom/client";
|
|
||||||
import { trigger, on } from "./events";
|
|
||||||
import Playlists from "./playlist";
|
|
||||||
import ws from "./ws";
|
|
||||||
import Controls from "./controls";
|
|
||||||
import Ambiance from "./ambiance";
|
|
||||||
import Volumes from "./volume";
|
|
||||||
import Modal from "./modal";
|
|
||||||
|
|
||||||
function Content() {
|
|
||||||
ws.onmessage = (e) => {
|
|
||||||
const data = JSON.parse(e.data);
|
|
||||||
trigger("dnd:" + data.event, data.payload);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Playlists />
|
|
||||||
<Controls />
|
|
||||||
<Ambiance />
|
|
||||||
<Volumes />
|
|
||||||
<Modal />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const domNode = document.getElementById("content");
|
|
||||||
const root = createRoot(domNode!);
|
|
||||||
root.render(<Content />);
|
|
|
@ -1,111 +0,0 @@
|
||||||
import React, { useState } from "react";
|
|
||||||
import { on } from "./events";
|
|
||||||
import ws from "./ws";
|
|
||||||
import { useEffect, useRef } from "react";
|
|
||||||
import { useDebounce } from "./hooks/useDebounce";
|
|
||||||
|
|
||||||
export default function Volumes() {
|
|
||||||
const [playlist, setPlaylist] = useState(0);
|
|
||||||
const [ambiance, setAmbiance] = useState(0);
|
|
||||||
|
|
||||||
const plfirstUpdate = useRef(true);
|
|
||||||
const ambfirstUpdate = useRef(true);
|
|
||||||
const plsend = useDebounce(playlist, 100);
|
|
||||||
const ambsend = useDebounce(ambiance, 100);
|
|
||||||
|
|
||||||
on("dnd:volume", (e: any) => {
|
|
||||||
setPlaylist(e.detail.playlist);
|
|
||||||
setAmbiance(e.detail.ambiance);
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (plfirstUpdate.current) {
|
|
||||||
plfirstUpdate.current = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ws.send(
|
|
||||||
JSON.stringify({
|
|
||||||
event: "vol_set",
|
|
||||||
payload: {
|
|
||||||
type: "playlist",
|
|
||||||
vol: plsend.toString(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}, [plsend]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (ambfirstUpdate.current) {
|
|
||||||
ambfirstUpdate.current = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ws.send(
|
|
||||||
JSON.stringify({
|
|
||||||
event: "vol_set",
|
|
||||||
payload: {
|
|
||||||
type: "ambiance",
|
|
||||||
vol: ambsend.toString(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}, [ambsend]);
|
|
||||||
|
|
||||||
const PlaylistVol = (e: any) => {
|
|
||||||
setPlaylist(parseFloat(e.target.value));
|
|
||||||
};
|
|
||||||
const AmbianceVol = (e: any) => {
|
|
||||||
setAmbiance(parseFloat(e.target.value));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<section>
|
|
||||||
<div id="volume_playlist" className="input-container">
|
|
||||||
<label htmlFor="playlist-volume">Playlist</label>
|
|
||||||
<input
|
|
||||||
type="range"
|
|
||||||
id="playlist-volume"
|
|
||||||
min="-10"
|
|
||||||
max="4"
|
|
||||||
step="0.1"
|
|
||||||
value={playlist}
|
|
||||||
onChange={PlaylistVol}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
id="playlist-volume-number"
|
|
||||||
type="number"
|
|
||||||
min="-10"
|
|
||||||
max="4"
|
|
||||||
step="0.1"
|
|
||||||
style={{ width: "50px" }}
|
|
||||||
value={playlist}
|
|
||||||
onChange={PlaylistVol}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div id="volume_ambiance" className="input-container">
|
|
||||||
<label htmlFor="ambiance-volume">Ambiance</label>
|
|
||||||
<input
|
|
||||||
type="range"
|
|
||||||
id="ambiance-volume"
|
|
||||||
min="-10"
|
|
||||||
max="4"
|
|
||||||
step="0.1"
|
|
||||||
value={ambiance}
|
|
||||||
onChange={AmbianceVol}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
id="ambiance-volume-number"
|
|
||||||
type="number"
|
|
||||||
min="-10"
|
|
||||||
max="4"
|
|
||||||
step="0.1"
|
|
||||||
style={{ width: "50px" }}
|
|
||||||
value={ambiance}
|
|
||||||
onChange={AmbianceVol}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
import ReconnectingWebSocket from "reconnecting-websocket";
|
|
||||||
|
|
||||||
const ws = new ReconnectingWebSocket(
|
|
||||||
(window.location.protocol === "https:" ? "wss://" : "ws://") +
|
|
||||||
window.location.host +
|
|
||||||
"/ws"
|
|
||||||
);
|
|
||||||
|
|
||||||
export default ws;
|
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"scripts": {
|
|
||||||
"build": "esbuild app/root.tsx --bundle --outfile=/public/script.js"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@types/react": "^18.2.33",
|
|
||||||
"@types/react-dom": "^18.2.14",
|
|
||||||
"array-move": "^4.0.0",
|
|
||||||
"esbuild": "^0.15.14",
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-beautiful-dnd": "^13.1.1",
|
|
||||||
"react-debounce-input": "^3.3.0",
|
|
||||||
"react-dnd": "^16.0.1",
|
|
||||||
"react-dnd-html5-backend": "^16.0.1",
|
|
||||||
"react-dom": "^18.2.0",
|
|
||||||
"react-easy-sort": "^1.6.0",
|
|
||||||
"react-sortable-hoc": "^2.0.0",
|
|
||||||
"reconnecting-websocket": "^4.4.0",
|
|
||||||
"sortablejs": "^1.15.0"
|
|
||||||
}
|
|
||||||
}
|
|
435
src/yarn.lock
435
src/yarn.lock
|
@ -1,435 +0,0 @@
|
||||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
|
||||||
# yarn lockfile v1
|
|
||||||
|
|
||||||
|
|
||||||
"@babel/runtime@^7.15.4", "@babel/runtime@^7.2.0", "@babel/runtime@^7.9.2":
|
|
||||||
version "7.23.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885"
|
|
||||||
integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==
|
|
||||||
dependencies:
|
|
||||||
regenerator-runtime "^0.14.0"
|
|
||||||
|
|
||||||
"@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==
|
|
||||||
|
|
||||||
"@react-dnd/asap@^5.0.1":
|
|
||||||
version "5.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-5.0.2.tgz#1f81f124c1cd6f39511c11a881cfb0f715343488"
|
|
||||||
integrity sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==
|
|
||||||
|
|
||||||
"@react-dnd/invariant@^4.0.1":
|
|
||||||
version "4.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/@react-dnd/invariant/-/invariant-4.0.2.tgz#b92edffca10a26466643349fac7cdfb8799769df"
|
|
||||||
integrity sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==
|
|
||||||
|
|
||||||
"@react-dnd/shallowequal@^4.0.1":
|
|
||||||
version "4.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz#d1b4befa423f692fa4abf1c79209702e7d8ae4b4"
|
|
||||||
integrity sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==
|
|
||||||
|
|
||||||
"@types/hoist-non-react-statics@^3.3.0":
|
|
||||||
version "3.3.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.4.tgz#cc477ce0283bb9d19ea0cbfa2941fe2c8493a1be"
|
|
||||||
integrity sha512-ZchYkbieA+7tnxwX/SCBySx9WwvWR8TaP5tb2jRAzwvLb/rWchGw3v0w3pqUbUvj0GCwW2Xz/AVPSk6kUGctXQ==
|
|
||||||
dependencies:
|
|
||||||
"@types/react" "*"
|
|
||||||
hoist-non-react-statics "^3.3.0"
|
|
||||||
|
|
||||||
"@types/prop-types@*":
|
|
||||||
version "15.7.9"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.9.tgz#b6f785caa7ea1fe4414d9df42ee0ab67f23d8a6d"
|
|
||||||
integrity sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==
|
|
||||||
|
|
||||||
"@types/react-dom@^18.2.14":
|
|
||||||
version "18.2.14"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.14.tgz#c01ba40e5bb57fc1dc41569bb3ccdb19eab1c539"
|
|
||||||
integrity sha512-V835xgdSVmyQmI1KLV2BEIUgqEuinxp9O4G6g3FqO/SqLac049E53aysv0oEFD2kHfejeKU+ZqL2bcFWj9gLAQ==
|
|
||||||
dependencies:
|
|
||||||
"@types/react" "*"
|
|
||||||
|
|
||||||
"@types/react-redux@^7.1.20":
|
|
||||||
version "7.1.28"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.28.tgz#30a44303c7daceb6ede9cfb4aaf72e64f1dde4de"
|
|
||||||
integrity sha512-EQr7cChVzVUuqbA+J8ArWK1H0hLAHKOs21SIMrskKZ3nHNeE+LFYA+IsoZGhVOT8Ktjn3M20v4rnZKN3fLbypw==
|
|
||||||
dependencies:
|
|
||||||
"@types/hoist-non-react-statics" "^3.3.0"
|
|
||||||
"@types/react" "*"
|
|
||||||
hoist-non-react-statics "^3.3.0"
|
|
||||||
redux "^4.0.0"
|
|
||||||
|
|
||||||
"@types/react@*", "@types/react@^18.2.33":
|
|
||||||
version "18.2.33"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.33.tgz#055356243dc4350a9ee6c6a2c07c5cae12e38877"
|
|
||||||
integrity sha512-v+I7S+hu3PIBoVkKGpSYYpiBT1ijqEzWpzQD62/jm4K74hPpSP7FF9BnKG6+fg2+62weJYkkBWDJlZt5JO/9hg==
|
|
||||||
dependencies:
|
|
||||||
"@types/prop-types" "*"
|
|
||||||
"@types/scheduler" "*"
|
|
||||||
csstype "^3.0.2"
|
|
||||||
|
|
||||||
"@types/scheduler@*":
|
|
||||||
version "0.16.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.5.tgz#4751153abbf8d6199babb345a52e1eb4167d64af"
|
|
||||||
integrity sha512-s/FPdYRmZR8SjLWGMCuax7r3qCWQw9QKHzXVukAuuIJkXkDRwp+Pu5LMIVFi0Fxbav35WURicYr8u1QsoybnQw==
|
|
||||||
|
|
||||||
array-move@^3.0.1:
|
|
||||||
version "3.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/array-move/-/array-move-3.0.1.tgz#179645cc0987b65953a4fc06b6df9045e4ba9618"
|
|
||||||
integrity sha512-H3Of6NIn2nNU1gsVDqDnYKY/LCdWvCMMOWifNGhKcVQgiZ6nOek39aESOvro6zmueP07exSl93YLvkN4fZOkSg==
|
|
||||||
|
|
||||||
array-move@^4.0.0:
|
|
||||||
version "4.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/array-move/-/array-move-4.0.0.tgz#2c3730f056cc926f62a59769a5a8cda2fb6d8c55"
|
|
||||||
integrity sha512-+RY54S8OuVvg94THpneQvFRmqWdAHeqtMzgMW6JNurHxe8rsS07cHQdfGkXnTUXiBcyZ0j3SiDIxxj0RPiqCkQ==
|
|
||||||
|
|
||||||
css-box-model@^1.2.0:
|
|
||||||
version "1.2.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1"
|
|
||||||
integrity sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==
|
|
||||||
dependencies:
|
|
||||||
tiny-invariant "^1.0.6"
|
|
||||||
|
|
||||||
csstype@^3.0.2:
|
|
||||||
version "3.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
|
|
||||||
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
|
|
||||||
|
|
||||||
dnd-core@^16.0.1:
|
|
||||||
version "16.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-16.0.1.tgz#a1c213ed08961f6bd1959a28bb76f1a868360d19"
|
|
||||||
integrity sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==
|
|
||||||
dependencies:
|
|
||||||
"@react-dnd/asap" "^5.0.1"
|
|
||||||
"@react-dnd/invariant" "^4.0.1"
|
|
||||||
redux "^4.2.0"
|
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
fast-deep-equal@^3.1.3:
|
|
||||||
version "3.1.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
|
||||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
|
||||||
|
|
||||||
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
|
|
||||||
version "3.3.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
|
||||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
|
||||||
dependencies:
|
|
||||||
react-is "^16.7.0"
|
|
||||||
|
|
||||||
invariant@^2.2.4:
|
|
||||||
version "2.2.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
|
|
||||||
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
|
|
||||||
dependencies:
|
|
||||||
loose-envify "^1.0.0"
|
|
||||||
|
|
||||||
"js-tokens@^3.0.0 || ^4.0.0":
|
|
||||||
version "4.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
|
||||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
|
||||||
|
|
||||||
lodash.debounce@^4:
|
|
||||||
version "4.0.8"
|
|
||||||
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
|
||||||
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
|
|
||||||
|
|
||||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
|
|
||||||
version "1.4.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
|
||||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
|
||||||
dependencies:
|
|
||||||
js-tokens "^3.0.0 || ^4.0.0"
|
|
||||||
|
|
||||||
memoize-one@^5.1.1:
|
|
||||||
version "5.2.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
|
|
||||||
integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
|
|
||||||
|
|
||||||
object-assign@^4.1.1:
|
|
||||||
version "4.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
|
||||||
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
|
|
||||||
|
|
||||||
prop-types@^15.5.7, prop-types@^15.7.2, prop-types@^15.8.1:
|
|
||||||
version "15.8.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
|
||||||
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
|
||||||
dependencies:
|
|
||||||
loose-envify "^1.4.0"
|
|
||||||
object-assign "^4.1.1"
|
|
||||||
react-is "^16.13.1"
|
|
||||||
|
|
||||||
raf-schd@^4.0.2:
|
|
||||||
version "4.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a"
|
|
||||||
integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==
|
|
||||||
|
|
||||||
react-beautiful-dnd@^13.1.1:
|
|
||||||
version "13.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz#b0f3087a5840920abf8bb2325f1ffa46d8c4d0a2"
|
|
||||||
integrity sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.9.2"
|
|
||||||
css-box-model "^1.2.0"
|
|
||||||
memoize-one "^5.1.1"
|
|
||||||
raf-schd "^4.0.2"
|
|
||||||
react-redux "^7.2.0"
|
|
||||||
redux "^4.0.4"
|
|
||||||
use-memo-one "^1.1.1"
|
|
||||||
|
|
||||||
react-debounce-input@^3.3.0:
|
|
||||||
version "3.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-debounce-input/-/react-debounce-input-3.3.0.tgz#85e3ebcaa41f2016e50613134a1ec9fe3cdb422e"
|
|
||||||
integrity sha512-VEqkvs8JvY/IIZvh71Z0TC+mdbxERvYF33RcebnodlsUZ8RSgyKe2VWaHXv4+/8aoOgXLxWrdsYs2hDhcwbUgA==
|
|
||||||
dependencies:
|
|
||||||
lodash.debounce "^4"
|
|
||||||
prop-types "^15.8.1"
|
|
||||||
|
|
||||||
react-dnd-html5-backend@^16.0.1:
|
|
||||||
version "16.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz#87faef15845d512a23b3c08d29ecfd34871688b6"
|
|
||||||
integrity sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==
|
|
||||||
dependencies:
|
|
||||||
dnd-core "^16.0.1"
|
|
||||||
|
|
||||||
react-dnd@^16.0.1:
|
|
||||||
version "16.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-16.0.1.tgz#2442a3ec67892c60d40a1559eef45498ba26fa37"
|
|
||||||
integrity sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==
|
|
||||||
dependencies:
|
|
||||||
"@react-dnd/invariant" "^4.0.1"
|
|
||||||
"@react-dnd/shallowequal" "^4.0.1"
|
|
||||||
dnd-core "^16.0.1"
|
|
||||||
fast-deep-equal "^3.1.3"
|
|
||||||
hoist-non-react-statics "^3.3.2"
|
|
||||||
|
|
||||||
react-dom@^18.2.0:
|
|
||||||
version "18.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
|
|
||||||
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
|
|
||||||
dependencies:
|
|
||||||
loose-envify "^1.1.0"
|
|
||||||
scheduler "^0.23.0"
|
|
||||||
|
|
||||||
react-easy-sort@^1.6.0:
|
|
||||||
version "1.6.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-easy-sort/-/react-easy-sort-1.6.0.tgz#b40cce827913f0640c1b2e5438dd4d007e26db32"
|
|
||||||
integrity sha512-zd9Nn90wVlZPEwJrpqElN87sf9GZnFR1StfjgNQVbSpR5QTSzCHjEYK6REuwq49Ip+76KOMSln9tg/ST2KLelg==
|
|
||||||
dependencies:
|
|
||||||
array-move "^3.0.1"
|
|
||||||
tslib "2.0.1"
|
|
||||||
|
|
||||||
react-is@^16.13.1, react-is@^16.7.0:
|
|
||||||
version "16.13.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
|
||||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
|
||||||
|
|
||||||
react-is@^17.0.2:
|
|
||||||
version "17.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
|
|
||||||
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
|
|
||||||
|
|
||||||
react-redux@^7.2.0:
|
|
||||||
version "7.2.9"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.9.tgz#09488fbb9416a4efe3735b7235055442b042481d"
|
|
||||||
integrity sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.15.4"
|
|
||||||
"@types/react-redux" "^7.1.20"
|
|
||||||
hoist-non-react-statics "^3.3.2"
|
|
||||||
loose-envify "^1.4.0"
|
|
||||||
prop-types "^15.7.2"
|
|
||||||
react-is "^17.0.2"
|
|
||||||
|
|
||||||
react-sortable-hoc@^2.0.0:
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz#f6780d8aa4b922a21f3e754af542f032677078b7"
|
|
||||||
integrity sha512-JZUw7hBsAHXK7PTyErJyI7SopSBFRcFHDjWW5SWjcugY0i6iH7f+eJkY8cJmGMlZ1C9xz1J3Vjz0plFpavVeRg==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.2.0"
|
|
||||||
invariant "^2.2.4"
|
|
||||||
prop-types "^15.5.7"
|
|
||||||
|
|
||||||
react@^18.2.0:
|
|
||||||
version "18.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
|
||||||
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
|
|
||||||
dependencies:
|
|
||||||
loose-envify "^1.1.0"
|
|
||||||
|
|
||||||
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==
|
|
||||||
|
|
||||||
redux@^4.0.0, redux@^4.0.4, redux@^4.2.0:
|
|
||||||
version "4.2.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197"
|
|
||||||
integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.9.2"
|
|
||||||
|
|
||||||
regenerator-runtime@^0.14.0:
|
|
||||||
version "0.14.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
|
|
||||||
integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==
|
|
||||||
|
|
||||||
scheduler@^0.23.0:
|
|
||||||
version "0.23.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
|
|
||||||
integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
|
|
||||||
dependencies:
|
|
||||||
loose-envify "^1.1.0"
|
|
||||||
|
|
||||||
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==
|
|
||||||
|
|
||||||
tiny-invariant@^1.0.6:
|
|
||||||
version "1.3.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642"
|
|
||||||
integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==
|
|
||||||
|
|
||||||
tslib@2.0.1:
|
|
||||||
version "2.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e"
|
|
||||||
integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==
|
|
||||||
|
|
||||||
use-memo-one@^1.1.1:
|
|
||||||
version "1.1.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99"
|
|
||||||
integrity sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==
|
|
|
@ -7,10 +7,73 @@
|
||||||
<title>D&D Music Bot!</title>
|
<title>D&D Music Bot!</title>
|
||||||
<link rel="stylesheet" href="/css/solarized-dark.css">
|
<link rel="stylesheet" href="/css/solarized-dark.css">
|
||||||
<link rel="stylesheet" href="/css/style.css">
|
<link rel="stylesheet" href="/css/style.css">
|
||||||
<link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAACNklEQVQ4jaWSy0vUcRTFP/f7G4cUUqzAZtQsgzYWiDMjjYgotbOtYLSIFlKBix7YH9DWXQaRFRREi8hN9MCFPZDUzB4IUkRo5Oho1CLTcEbne1o0UWCI0FndC+cezr33wH/C/m5mkvFOw04JIsBrmXVVDI0OryfgfhepZH0bWA/wFaMLqDXp/nQyuWVDAhi1wIz36k5n/BXgMVAamI+vJxD6o7TS61Uw7QLbEXH+OGIOQzmtTG7IgVfBPjOdE6oXdtrQd8mOVQ6/+vCbM9XcvGmipia8xsFsLLZN6DBQZWIRKJPZISd5gLFYrCBaYF3KLB0IFxdVzyQTZYZukNXZEEA2CDaH8SUS5zHbBVZo+AE5OzrXEL+eyxHxZo2IyzL/FqywcnhsFCCktrYgnZrqWc35nsCCqHM2mJMWkXuD+RVPUIvzRxB3K0Ze3F5zg8/pj1XCEkEQ9OHoZnH5uaFAYfXjrcmhUkPvyit39v7zC9lcKO1Y3QoEwOD28fEl4CRAqiFxJyddAHuUnpwsnW9MFIdW3JeMW41Ghl++N/AOMuX5YTCqlU/nbCxWZLDXQRfQRNiNr+Z0L+OUFHZwNhm/CRBaWMh8Ki0u+iYoQeyZ3Z+4lcKeytSBqPOyZ2a+X1grWESoE6wFWAZwNRMTWaEzBk/yLtrNdAmow7gaHRm96HEpYCC/divGjGEdv+h5qLk5lM4utUvWAvxw8g8iI2MPAeYbE9VeFMrbNZl2GzoRHRrrWy+hG8ZPg13xb+XS+CIAAAAASUVORK5CYII=">
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="content" class="container"></div>
|
<div class="container">
|
||||||
<script src="/public/script.js"></script>
|
<h2 class="bot">Playlists</h2>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div id="items" class="item-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 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>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<h2 class="bot">Ambiance</h2>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div id="ambiance" class="item-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 -->
|
||||||
|
<div class="modal-content">
|
||||||
|
<p>Waiting for bot..</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="/public/script.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -1,52 +0,0 @@
|
||||||
# audio_output {
|
|
||||||
# type "fifo"
|
|
||||||
# name "fifo-output"
|
|
||||||
# path "{{ .fifo }}"
|
|
||||||
# format "48000:16:2"
|
|
||||||
# enabled "yes"
|
|
||||||
#}
|
|
||||||
audio_output {
|
|
||||||
type "snapcast"
|
|
||||||
name "playlist"
|
|
||||||
bind_to_address "0.0.0.0"
|
|
||||||
port "{{ .playlist }}"
|
|
||||||
zeroconf "no"
|
|
||||||
format "48000:16:2"
|
|
||||||
enabled "yes"
|
|
||||||
always_on "yes"
|
|
||||||
}
|
|
||||||
audio_output {
|
|
||||||
type "snapcast"
|
|
||||||
name "ambiance"
|
|
||||||
bind_to_address "0.0.0.0"
|
|
||||||
port "{{ .ambiance }}"
|
|
||||||
zeroconf "no"
|
|
||||||
format "48000:16:2"
|
|
||||||
enabled "yes"
|
|
||||||
always_on "yes"
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
plugin "curl"
|
|
||||||
verbose "yes"
|
|
||||||
}
|
|
||||||
decoder {
|
|
||||||
plugin "wildmidi"
|
|
||||||
enabled "no"
|
|
||||||
}
|
|
||||||
decoder {
|
|
||||||
plugin "hybrid_dsd"
|
|
||||||
enabled "no"
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
plugin "qobuz"
|
|
||||||
enabled "no"
|
|
||||||
}
|
|
||||||
resampler {
|
|
||||||
plugin "soxr"
|
|
||||||
quality "very high"
|
|
||||||
}
|
|
||||||
|
|
||||||
volume_normalization "yes"
|
|
||||||
# audio_output_format "48000:16:2"
|
|
||||||
bind_to_address "{{ .sock }}"
|
|
||||||
pid_file "{{ .pid }}"
|
|
62
ws.go
62
ws.go
|
@ -12,21 +12,18 @@ import (
|
||||||
"github.com/kataras/go-events"
|
"github.com/kataras/go-events"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Websocket struct {
|
|
||||||
sync.Mutex
|
|
||||||
|
|
||||||
clients *bcast.Group
|
|
||||||
}
|
|
||||||
|
|
||||||
var ws *Websocket
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
log.Println("ws.go loading..")
|
log.Println("ws.go loading..")
|
||||||
ws = new(Websocket)
|
go ws_clients.Broadcast(0)
|
||||||
|
ws_msg = make(chan interface{})
|
||||||
|
|
||||||
ws.clients = bcast.NewGroup()
|
go func() {
|
||||||
|
var msg interface{}
|
||||||
go ws.clients.Broadcast(0)
|
for {
|
||||||
|
msg = <-ws_msg
|
||||||
|
ws_clients.Send(msg)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
log.Println("ws.go done.")
|
log.Println("ws.go done.")
|
||||||
}
|
}
|
||||||
|
@ -36,19 +33,12 @@ type WSmsg struct {
|
||||||
Payload json.RawMessage
|
Payload json.RawMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
type Event struct {
|
var ws_clients = bcast.NewGroup()
|
||||||
Event string `json:"event"`
|
var ws_msg chan interface{}
|
||||||
Payload any `json:"payload,omitempty"`
|
var WSMutex = &sync.Mutex{}
|
||||||
}
|
|
||||||
|
|
||||||
func (ws *Websocket) SendEvent(e Event) {
|
func handleWS(c *websocket.Conn) error {
|
||||||
ws.Lock()
|
memb := ws_clients.Join()
|
||||||
ws.clients.Send(e)
|
|
||||||
ws.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ws *Websocket) join(c *websocket.Conn) error {
|
|
||||||
memb := ws.clients.Join()
|
|
||||||
defer memb.Close()
|
defer memb.Close()
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
@ -74,27 +64,21 @@ func (ws *Websocket) join(c *websocket.Conn) error {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
msg, err := app.songInfoEvent("song_info")
|
msg := app.songInfoEvent("song_info")
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
vol := Event{"volume", map[string]float64{
|
|
||||||
"playlist": pl_volume.Volume,
|
|
||||||
"ambiance": amb_volume.Volume,
|
|
||||||
}}
|
|
||||||
|
|
||||||
c.SetWriteDeadline(time.Now().Add(10 * time.Second))
|
c.SetWriteDeadline(time.Now().Add(10 * time.Second))
|
||||||
c.WriteJSON(msg)
|
c.WriteJSON(msg)
|
||||||
c.WriteJSON(vol)
|
|
||||||
|
|
||||||
if app.ambiance.Len() > 0 {
|
if app.ambiance.Len() > 0 {
|
||||||
msg := Event{"ambiance_play", map[string]string{
|
msg := make(map[string]interface{})
|
||||||
"id": app.curamb.Id,
|
out := make(map[string]interface{})
|
||||||
}}
|
msg["event"] = "ambiance_play"
|
||||||
|
out["type"] = app.curamb
|
||||||
|
msg["payload"] = out
|
||||||
c.WriteJSON(msg)
|
c.WriteJSON(msg)
|
||||||
} else {
|
} else {
|
||||||
msg := Event{"ambiance_stop", nil}
|
msg := make(map[string]interface{})
|
||||||
|
msg["event"] = "ambiance_stop"
|
||||||
c.WriteJSON(msg)
|
c.WriteJSON(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,6 +89,6 @@ func (ws *Websocket) join(c *websocket.Conn) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
app.events.Emit(events.EventName(msg.Event), msg.Payload, memb)
|
app.events.Emit(events.EventName(msg.Event), msg.Payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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==
|
196
youtube.go
196
youtube.go
|
@ -2,81 +2,155 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"dndmusicbot/youtube"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"math/big"
|
||||||
"os"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafov/m3u8"
|
mrand "math/rand"
|
||||||
"github.com/julienschmidt/httprouter"
|
|
||||||
|
"github.com/sosodev/duration"
|
||||||
|
"google.golang.org/api/option"
|
||||||
|
"google.golang.org/api/youtube/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
log.Println("youtube.go loading..")
|
log.Println("youtube.go loading..")
|
||||||
|
|
||||||
app.youtube = youtube.New()
|
var err error
|
||||||
|
|
||||||
|
apikey := config.GetString("youtube.apikey")
|
||||||
|
|
||||||
|
app.youtube, err = youtube.NewService(context.Background(), option.WithAPIKey(apikey))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
log.Println("youtube.go done.")
|
log.Println("youtube.go done.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProxyTube(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
func ShufflePlaylist(list []string) ([]string, error) {
|
||||||
os.MkdirAll("cache", 0755)
|
seedb := make([]byte, 32)
|
||||||
_, data, err := app.cache.GetOrCreateBytes("youtube."+p.ByName("id"), func() (out []byte, err error) {
|
_, err := rand.Read(seedb)
|
||||||
vinfo, err := app.youtube.GetVideoFromID(p.ByName("id"))
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
uri := vinfo.GetHLSPlaylist("234")
|
|
||||||
|
|
||||||
resp, err := http.Get(uri)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
pl, _, err := m3u8.DecodeFrom(resp.Body, true)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var segresp *http.Response
|
|
||||||
mediapl := pl.(*m3u8.MediaPlaylist)
|
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
for _, segment := range mediapl.GetAllSegments() {
|
|
||||||
segresp, err = http.Get(segment.URI)
|
|
||||||
if err != nil {
|
|
||||||
segresp.Body.Close()
|
|
||||||
log.Println(err)
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err = io.Copy(buf, segresp.Body)
|
|
||||||
if err != nil {
|
|
||||||
segresp.Body.Close()
|
|
||||||
log.Println(err)
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
return nil, err
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
}
|
||||||
return
|
seed := binary.BigEndian.Uint64(seedb)
|
||||||
|
mrand.Seed(int64(seed))
|
||||||
|
mrand.Shuffle(len(list), func(i, j int) { list[i], list[j] = list[j], list[i] })
|
||||||
|
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *App) GetSong(list []string) string {
|
||||||
|
return list[app.plidx]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *App) GetNextSong(list []string) string {
|
||||||
|
app.plidx++
|
||||||
|
if app.plidx >= len(app.active) {
|
||||||
|
app.plidx = 0
|
||||||
|
}
|
||||||
|
return list[app.plidx]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *App) GetPrevSong(list []string) string {
|
||||||
|
app.plidx--
|
||||||
|
if app.plidx < 0 {
|
||||||
|
app.plidx = len(list)
|
||||||
|
}
|
||||||
|
return list[app.plidx]
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetRandomSong(list []string) string {
|
||||||
|
if !(len(list) > 0) {
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
seeker := bytes.NewReader(data)
|
idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(list)-1)))
|
||||||
w.Header().Add("Content-Type", "audio/mp4")
|
if err != nil {
|
||||||
http.ServeContent(w, r, "youtube."+p.ByName("id"), time.Now(), seeker)
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app App) Playlist(playlist string) ([]string, error) {
|
||||||
|
call := app.youtube.PlaylistItems.List([]string{"contentDetails"})
|
||||||
|
pageToken := ""
|
||||||
|
call = call.MaxResults(50)
|
||||||
|
call = call.PlaylistId(playlist)
|
||||||
|
if pageToken != "" {
|
||||||
|
call = call.PageToken(pageToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
var list []string
|
||||||
|
for {
|
||||||
|
response, err := call.Do()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, item := range response.Items {
|
||||||
|
list = append(list, item.ContentDetails.VideoId)
|
||||||
|
}
|
||||||
|
pageToken = response.NextPageToken
|
||||||
|
if pageToken == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
call.PageToken(pageToken)
|
||||||
|
}
|
||||||
|
return list, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,150 +0,0 @@
|
||||||
package youtube
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/grafov/m3u8"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Client struct {
|
|
||||||
InnertubeClient *InnertubeClient
|
|
||||||
HTTPClient *http.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
type InnertubeClient struct {
|
|
||||||
Key string `json:"-"`
|
|
||||||
HDRClientName string `json:"-"`
|
|
||||||
HL string `json:"hl,omitempty"`
|
|
||||||
GL string `json:"gl,omitempty"`
|
|
||||||
ClientName string `json:"clientName,omitempty"`
|
|
||||||
ClientVersion string `json:"clientVersion,omitempty"`
|
|
||||||
DeviceModel string `json:"deviceModel,omitempty"`
|
|
||||||
UserAgent string `json:"userAgent,omitempty"`
|
|
||||||
TimeZone string `json:"timeZone,omitempty"`
|
|
||||||
UTCOffsetMins int64 `json:"utcOffsetMinutes,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type innertubeRequest struct {
|
|
||||||
VideoID string `json:"videoId,omitempty"`
|
|
||||||
BrowseID string `json:"browseId,omitempty"`
|
|
||||||
Continuation string `json:"continuation,omitempty"`
|
|
||||||
Context struct {
|
|
||||||
Client InnertubeClient `json:"client"`
|
|
||||||
} `json:"context"`
|
|
||||||
PlaybackContext struct {
|
|
||||||
ContentPlaybackContext struct {
|
|
||||||
HTML5Preference string `json:"html5Preference"`
|
|
||||||
} `json:"contentPlaybackContext"`
|
|
||||||
} `json:"playbackContext,omitempty"`
|
|
||||||
ContentCheckOK bool `json:"contentCheckOk,omitempty"`
|
|
||||||
RacyCheckOk bool `json:"racyCheckOk,omitempty"`
|
|
||||||
Params string `json:"params"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
IOSClient = InnertubeClient{
|
|
||||||
Key: "AIzaSyAOghZGza2MQSZkY_zfZ370N-PUdXEo8AI",
|
|
||||||
HDRClientName: "5",
|
|
||||||
HL: "en",
|
|
||||||
GL: "US",
|
|
||||||
ClientName: "IOS",
|
|
||||||
ClientVersion: "17.33.2",
|
|
||||||
DeviceModel: "iPhone14,3",
|
|
||||||
UserAgent: "com.google.ios.youtube/17.33.2 (iPhone14,3; U; CPU iOS 15_6 like Mac OS X)",
|
|
||||||
TimeZone: "UTC",
|
|
||||||
UTCOffsetMins: 0,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
var DefaultClient = IOSClient
|
|
||||||
|
|
||||||
func New() *Client {
|
|
||||||
client := new(Client)
|
|
||||||
|
|
||||||
client.HTTPClient = &http.Client{}
|
|
||||||
client.InnertubeClient = &DefaultClient
|
|
||||||
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Client) GetVideoFromID(id string) (out Video, err error) {
|
|
||||||
uri, _ := url.Parse("https://www.youtube.com/youtubei/v1/player")
|
|
||||||
q := uri.Query()
|
|
||||||
q.Add("key", c.InnertubeClient.Key)
|
|
||||||
uri.RawQuery = q.Encode()
|
|
||||||
|
|
||||||
reqData := new(innertubeRequest)
|
|
||||||
reqData.Context.Client = *c.InnertubeClient
|
|
||||||
reqData.VideoID = id
|
|
||||||
reqData.ContentCheckOK = true
|
|
||||||
reqData.RacyCheckOk = true
|
|
||||||
reqData.Params = "CgIQBg=="
|
|
||||||
reqData.PlaybackContext.ContentPlaybackContext.HTML5Preference = "HTML5_PREF_WANTS"
|
|
||||||
|
|
||||||
reqBody, err := json.Marshal(reqData)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
payload := bytes.NewReader(reqBody)
|
|
||||||
req, err := http.NewRequest("POST", uri.String(), payload)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Header.Add("User-Agent", c.InnertubeClient.UserAgent)
|
|
||||||
req.Header.Add("X-YouTube-Client-Name", c.InnertubeClient.HDRClientName)
|
|
||||||
req.Header.Add("X-YouTube-Client-Version", c.InnertubeClient.ClientVersion)
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
|
||||||
|
|
||||||
res, err := c.HTTPClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer res.Body.Close()
|
|
||||||
|
|
||||||
json.NewDecoder(res.Body).Decode(&out)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
func (v Video) GetUrlByItag(itagNo int64) string {
|
|
||||||
for _, format := range v.StreamingData.AdaptiveFormats {
|
|
||||||
if format.Itag == itagNo {
|
|
||||||
return format.URL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v Video) GetHLSPlaylist(itag string) (out string) {
|
|
||||||
resp, err := http.Get(v.StreamingData.HlsManifestURL)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
p, _, err := m3u8.DecodeFrom(resp.Body, true)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pl := p.(*m3u8.MasterPlaylist)
|
|
||||||
for _, variant := range pl.Variants {
|
|
||||||
if len(variant.Alternatives) > 0 {
|
|
||||||
for _, a := range variant.Alternatives {
|
|
||||||
if a.GroupId == itag {
|
|
||||||
return a.URI
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
package youtube
|
|
||||||
|
|
||||||
type Video struct {
|
|
||||||
StreamingData struct {
|
|
||||||
AdaptiveFormats []struct {
|
|
||||||
ApproxDurationMs string `json:"approxDurationMs"`
|
|
||||||
AudioChannels int64 `json:"audioChannels"`
|
|
||||||
AudioQuality string `json:"audioQuality"`
|
|
||||||
AudioSampleRate string `json:"audioSampleRate"`
|
|
||||||
AverageBitrate int64 `json:"averageBitrate"`
|
|
||||||
Bitrate int64 `json:"bitrate"`
|
|
||||||
ColorInfo struct {
|
|
||||||
MatrixCoefficients string `json:"matrixCoefficients"`
|
|
||||||
Primaries string `json:"primaries"`
|
|
||||||
TransferCharacteristics string `json:"transferCharacteristics"`
|
|
||||||
} `json:"colorInfo"`
|
|
||||||
ContentLength string `json:"contentLength"`
|
|
||||||
Fps int64 `json:"fps"`
|
|
||||||
Height int64 `json:"height"`
|
|
||||||
HighReplication bool `json:"highReplication"`
|
|
||||||
IndexRange struct {
|
|
||||||
End string `json:"end"`
|
|
||||||
Start string `json:"start"`
|
|
||||||
} `json:"indexRange"`
|
|
||||||
InitRange struct {
|
|
||||||
End string `json:"end"`
|
|
||||||
Start string `json:"start"`
|
|
||||||
} `json:"initRange"`
|
|
||||||
Itag int64 `json:"itag"`
|
|
||||||
LastModified string `json:"lastModified"`
|
|
||||||
LoudnessDB float64 `json:"loudnessDb"`
|
|
||||||
MimeType string `json:"mimeType"`
|
|
||||||
ProjectionType string `json:"projectionType"`
|
|
||||||
Quality string `json:"quality"`
|
|
||||||
QualityLabel string `json:"qualityLabel"`
|
|
||||||
URL string `json:"url"`
|
|
||||||
Width int64 `json:"width"`
|
|
||||||
} `json:"adaptiveFormats"`
|
|
||||||
AspectRatio float64 `json:"aspectRatio"`
|
|
||||||
ExpiresInSeconds string `json:"expiresInSeconds"`
|
|
||||||
HlsManifestURL string `json:"hlsManifestUrl"`
|
|
||||||
ServerAbrStreamingURL string `json:"serverAbrStreamingUrl"`
|
|
||||||
} `json:"streamingData"`
|
|
||||||
VideoDetails struct {
|
|
||||||
AllowRatings bool `json:"allowRatings"`
|
|
||||||
Author string `json:"author"`
|
|
||||||
ChannelID string `json:"channelId"`
|
|
||||||
IsCrawlable bool `json:"isCrawlable"`
|
|
||||||
IsLiveContent bool `json:"isLiveContent"`
|
|
||||||
IsOwnerViewing bool `json:"isOwnerViewing"`
|
|
||||||
IsPrivate bool `json:"isPrivate"`
|
|
||||||
IsUnpluggedCorpus bool `json:"isUnpluggedCorpus"`
|
|
||||||
Keywords []string `json:"keywords"`
|
|
||||||
LengthSeconds string `json:"lengthSeconds"`
|
|
||||||
ShortDescription string `json:"shortDescription"`
|
|
||||||
Thumbnail struct {
|
|
||||||
Thumbnails []struct {
|
|
||||||
Height int64 `json:"height"`
|
|
||||||
URL string `json:"url"`
|
|
||||||
Width int64 `json:"width"`
|
|
||||||
} `json:"thumbnails"`
|
|
||||||
} `json:"thumbnail"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
VideoID string `json:"videoId"`
|
|
||||||
ViewCount string `json:"viewCount"`
|
|
||||||
} `json:"videoDetails"`
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package youtube
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNew(t *testing.T) {
|
|
||||||
client := New()
|
|
||||||
video, err := client.GetVideoFromID("LHsGz91Ivwg")
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(video.GetHLSPlaylist("234"))
|
|
||||||
}
|
|
145
ytdl.go
145
ytdl.go
|
@ -1,45 +1,25 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
)
|
)
|
||||||
|
|
||||||
var prate = rate.Sometimes{Interval: 1 * time.Second}
|
var prate = rate.Sometimes{Interval: 1 * time.Second}
|
||||||
|
var yturl = "https://youtu.be/%s"
|
||||||
|
|
||||||
// var yturl = "https://youtu.be/%s"
|
func NewYTdl(vid string) ([]byte, error) {
|
||||||
|
|
||||||
func YTUrl(uri string) (vid string, err error) {
|
|
||||||
u, err := url.Parse(uri)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch u.Host {
|
|
||||||
case "youtu.be":
|
|
||||||
vid = u.Path[1:]
|
|
||||||
case "m.youtube.com":
|
|
||||||
fallthrough
|
|
||||||
case "youtube.com":
|
|
||||||
fallthrough
|
|
||||||
case "www.youtube.com":
|
|
||||||
vid = u.Query().Get("v")
|
|
||||||
}
|
|
||||||
|
|
||||||
if vid == "" {
|
|
||||||
return vid, fmt.Errorf("unable to parse vid")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
func NewYTdlUrl(vid string) ([]byte, error) {
|
|
||||||
ytdl := config.GetString("youtube.ytdl")
|
ytdl := config.GetString("youtube.ytdl")
|
||||||
yt := exec.Command(
|
uri, err := exec.Command(
|
||||||
ytdl,
|
ytdl,
|
||||||
fmt.Sprintf(yturl, vid),
|
fmt.Sprintf(yturl, vid),
|
||||||
"--cookies", "./cookies.txt",
|
"--cookies", "./cookies.txt",
|
||||||
|
@ -48,13 +28,9 @@ func NewYTdlUrl(vid string) ([]byte, error) {
|
||||||
"--ignore-errors",
|
"--ignore-errors",
|
||||||
"--newline",
|
"--newline",
|
||||||
"--restrict-filenames",
|
"--restrict-filenames",
|
||||||
"-f", "234",
|
"-f", "140",
|
||||||
"--get-url",
|
"--get-url",
|
||||||
)
|
).Output()
|
||||||
|
|
||||||
yt.Stderr = os.Stderr
|
|
||||||
|
|
||||||
uri, err := yt.Output()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -62,100 +38,16 @@ func NewYTdlUrl(vid string) ([]byte, error) {
|
||||||
return uri[:len(uri)-1], nil
|
return uri[:len(uri)-1], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewYTdl(vid string) ([]byte, error) {
|
func DownloadAmbiance(uri string, name string) error {
|
||||||
tmpfile, err := exec.Command("mktemp", "/tmp/dnd_ytdlp_XXXXXXXXXXXX.m4a").Output()
|
ytdl := config.GetString("youtube.ytdl")
|
||||||
|
|
||||||
|
tmpfile, err := exec.Command("mktemp", "/tmp/dnd_XXXXXXXXXXXX.aac").Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpfile = tmpfile[:len(tmpfile)-1]
|
tmpfile = tmpfile[:len(tmpfile)-1]
|
||||||
|
|
||||||
ytdl := config.GetString("youtube.ytdl")
|
|
||||||
yt := exec.Command(
|
|
||||||
ytdl,
|
|
||||||
fmt.Sprintf(yturl, vid),
|
|
||||||
"-q",
|
|
||||||
"--cookies", "./cookies.txt",
|
|
||||||
"--no-call-home",
|
|
||||||
"--no-cache-dir",
|
|
||||||
"--ignore-errors",
|
|
||||||
"--newline",
|
|
||||||
"--restrict-filenames",
|
|
||||||
"--force-overwrites",
|
|
||||||
"--progress",
|
|
||||||
"--progress-template", "download:{ \"dl_bytes\": \"%(progress.downloaded_bytes)s\", \"total_bytes\": \"%(progress.total_bytes)s\" }",
|
|
||||||
"-f", "251",
|
|
||||||
"-o", string(tmpfile),
|
|
||||||
)
|
|
||||||
|
|
||||||
yt.Stderr = os.Stderr
|
|
||||||
ytprogress, err := yt.StdoutPipe()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = yt.Start()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Start ffmpeg to extract audio to %s", string(tmpfile))
|
|
||||||
msg := make(map[string]interface{})
|
|
||||||
msg["event"] = "ambiance_download_start"
|
|
||||||
data := make(map[string]string)
|
|
||||||
data["name"] = fmt.Sprintf(yturl, vid)
|
|
||||||
msg["payload"] = data
|
|
||||||
ws_msg <- msg
|
|
||||||
|
|
||||||
msg = make(map[string]interface{})
|
|
||||||
msg["event"] = "ambiance_download_progress"
|
|
||||||
data = make(map[string]string)
|
|
||||||
data["name"] = fmt.Sprintf(yturl, vid)
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(ytprogress)
|
|
||||||
|
|
||||||
for scanner.Scan() {
|
|
||||||
err := json.Unmarshal(scanner.Bytes(), &data)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
dl, _ := strconv.ParseFloat(data["dl_bytes"], 64)
|
|
||||||
total, _ := strconv.ParseFloat(data["total_bytes"], 64)
|
|
||||||
percent := math.Floor((dl / total) * 100)
|
|
||||||
data["percent"] = strconv.FormatInt(int64(percent), 10)
|
|
||||||
|
|
||||||
prate.Do(func() {
|
|
||||||
msg["payload"] = data
|
|
||||||
ws_msg <- msg
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = yt.Wait()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg = make(map[string]interface{})
|
|
||||||
msg["event"] = "ambiance_download_complete"
|
|
||||||
data = make(map[string]string)
|
|
||||||
data["name"] = fmt.Sprintf(yturl, vid)
|
|
||||||
msg["payload"] = data
|
|
||||||
ws_msg <- msg
|
|
||||||
|
|
||||||
return tmpfile, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
func DownloadAmbiance(uri string, name string) error {
|
|
||||||
ytdl := config.GetString("youtube.ytdl")
|
|
||||||
|
|
||||||
cmd := exec.Command(
|
cmd := exec.Command(
|
||||||
ytdl,
|
ytdl,
|
||||||
uri,
|
uri,
|
||||||
|
@ -163,7 +55,7 @@ func DownloadAmbiance(uri string, name string) error {
|
||||||
"--no-cache-dir",
|
"--no-cache-dir",
|
||||||
"-f", "140",
|
"-f", "140",
|
||||||
"--cookies", "../cookies.txt",
|
"--cookies", "../cookies.txt",
|
||||||
"-o", "-",
|
"-o", string(tmpfile),
|
||||||
"--force-overwrites",
|
"--force-overwrites",
|
||||||
"-q",
|
"-q",
|
||||||
"--progress",
|
"--progress",
|
||||||
|
@ -288,4 +180,3 @@ func DownloadAmbiance(uri string, name string) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
Loading…
Reference in New Issue