Compare commits
5 Commits
4156c7dc6d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9174c1b23a | ||
|
|
154c6a31b9 | ||
|
|
c8f9832f48 | ||
|
|
7bdfcf9776 | ||
|
|
a5757c3079 |
7
Makefile
7
Makefile
@@ -12,6 +12,9 @@ debug:
|
|||||||
$(GO) build -o $(EXE) -gcflags=all="-N -l" cmd/$(EXE)/*.go
|
$(GO) build -o $(EXE) -gcflags=all="-N -l" cmd/$(EXE)/*.go
|
||||||
dlv exec ./dischord
|
dlv exec ./dischord
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
find . -type f -name '*.go' -exec gofmt -w '{}' ';'
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
mkdir -p $(DESTDIR)$(PREFIX)/bin
|
mkdir -p $(DESTDIR)$(PREFIX)/bin
|
||||||
cp -f $(EXE) $(DESTDIR)$(PREFIX)/bin
|
cp -f $(EXE) $(DESTDIR)$(PREFIX)/bin
|
||||||
@@ -48,9 +51,9 @@ uninstall:
|
|||||||
rm -f $(DESTDIR)$(CFGPREFIX)/systemd/system/$(EXE).service
|
rm -f $(DESTDIR)$(CFGPREFIX)/systemd/system/$(EXE).service
|
||||||
|
|
||||||
test:
|
test:
|
||||||
$(GO) test -count=1 -v $(EXE)/extractor
|
$(GO) test -count=1 -v git.nobrain.org/r4/dischord/extractor/
|
||||||
|
|
||||||
.PHONY: all debug install uninstall clean
|
.PHONY: all debug fmt install uninstall clean
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(EXE)
|
rm -f $(EXE)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"git.nobrain.org/r4/dischord/player"
|
"git.nobrain.org/r4/dischord/player"
|
||||||
"git.nobrain.org/r4/dischord/util"
|
"git.nobrain.org/r4/dischord/util"
|
||||||
|
|
||||||
|
_ "embed"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -21,7 +22,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
_ "embed"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var copyright bool
|
var copyright bool
|
||||||
@@ -147,7 +147,7 @@ func main() {
|
|||||||
cfgfile := "config.toml"
|
cfgfile := "config.toml"
|
||||||
var cfg *config.Config
|
var cfg *config.Config
|
||||||
var err error
|
var err error
|
||||||
if autoconf || func() bool {cfg, err = config.Load(cfgfile); return err != nil}() {
|
if autoconf || func() bool { cfg, err = config.Load(cfgfile); return err != nil }() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
fmt.Println("Configuration file not found, launching automatic configurator.")
|
fmt.Println("Configuration file not found, launching automatic configurator.")
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Token string `toml:"bot-token"`
|
Token string `toml:"bot-token"`
|
||||||
FfmpegPath string `toml:"ffmpeg-path"`
|
FfmpegPath string `toml:"ffmpeg-path"`
|
||||||
Extractors extractor.Config `toml:"extractors"`
|
Extractors extractor.Config `toml:"extractors"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,14 +97,20 @@ func macosEnableExecutable(filename string) error {
|
|||||||
// configuration file does not exist or is invalid.
|
// configuration file does not exist or is invalid.
|
||||||
func Load(filename string) (*Config, error) {
|
func Load(filename string) (*Config, error) {
|
||||||
cfg := &Config{}
|
cfg := &Config{}
|
||||||
_, err := toml.DecodeFile(filename, cfg)
|
meta, err := toml.DecodeFile(filename, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if pe, ok := err.(toml.ParseError); ok {
|
||||||
|
fmt.Println(pe.ErrorWithUsage())
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if undec := meta.Undecoded(); len(undec) > 0 {
|
||||||
|
return nil, fmt.Errorf("%v: field '%v' could not be decoded", filename, undec[0])
|
||||||
|
}
|
||||||
if cfg.Token == defaultToken || cfg.Token == "" {
|
if cfg.Token == defaultToken || cfg.Token == "" {
|
||||||
return nil, ErrTokenNotSet
|
return nil, ErrTokenNotSet
|
||||||
}
|
}
|
||||||
if err := cfg.Extractors.CheckTypes(); err != nil {
|
if err := cfg.Extractors.CheckValidity(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if _, err := exec.LookPath(cfg.Extractors["youtube-dl"]["youtube-dl-path"].(string)); err != nil {
|
if _, err := exec.LookPath(cfg.Extractors["youtube-dl"]["youtube-dl-path"].(string)); err != nil {
|
||||||
@@ -125,7 +131,7 @@ func Autoconf(filename string) (*Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
download := func(executable bool, urlsByOS map[string]map[string]string) (filename string, err error) {
|
download := func(executable bool, urlsByOS map[string]map[string]string) (filename string, err error) {
|
||||||
filename, err = download(executable, urlsByOS, func(progress float32){
|
filename, err = download(executable, urlsByOS, func(progress float32) {
|
||||||
fmt.Printf("Progress: %.1f%%\r", progress*100.0)
|
fmt.Printf("Progress: %.1f%%\r", progress*100.0)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -152,18 +158,18 @@ func Autoconf(filename string) (*Config, error) {
|
|||||||
if youtubeDlPath == "" {
|
if youtubeDlPath == "" {
|
||||||
fmt.Println("Downloading youtube-dl")
|
fmt.Println("Downloading youtube-dl")
|
||||||
filename, err := download(true, map[string]map[string]string{
|
filename, err := download(true, map[string]map[string]string{
|
||||||
"windows": {
|
"windows": {
|
||||||
"amd64": "https://yt-dl.org/downloads/latest/youtube-dl.exe",
|
"amd64": "https://yt-dl.org/downloads/latest/youtube-dl.exe",
|
||||||
"386": "https://yt-dl.org/downloads/latest/youtube-dl.exe",
|
"386": "https://yt-dl.org/downloads/latest/youtube-dl.exe",
|
||||||
},
|
},
|
||||||
"any": {
|
"any": {
|
||||||
"any": "https://yt-dl.org/downloads/latest/youtube-dl",
|
"any": "https://yt-dl.org/downloads/latest/youtube-dl",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
youtubeDlPath = "./"+filename
|
youtubeDlPath = "./" + filename
|
||||||
macosEnableExecutable(youtubeDlPath)
|
macosEnableExecutable(youtubeDlPath)
|
||||||
if python3IsPython {
|
if python3IsPython {
|
||||||
// Replace first line with `replacement`
|
// Replace first line with `replacement`
|
||||||
@@ -195,21 +201,21 @@ func Autoconf(filename string) (*Config, error) {
|
|||||||
}
|
}
|
||||||
fmt.Println("Downloading FFmpeg")
|
fmt.Println("Downloading FFmpeg")
|
||||||
filename, err := download(false, map[string]map[string]string{
|
filename, err := download(false, map[string]map[string]string{
|
||||||
"linux": {
|
"linux": {
|
||||||
"amd64": "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz",
|
"amd64": "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz",
|
||||||
"386": "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-i686-static.tar.xz",
|
"386": "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-i686-static.tar.xz",
|
||||||
"arm64": "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-arm64-static.tar.xz",
|
"arm64": "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-arm64-static.tar.xz",
|
||||||
"arm": "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-armhf-static.tar.xz",
|
"arm": "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-armhf-static.tar.xz",
|
||||||
},
|
},
|
||||||
"windows": {
|
"windows": {
|
||||||
"amd64": "https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip",
|
"amd64": "https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip",
|
||||||
"386": "https://github.com/sudo-nautilus/FFmpeg-Builds-Win32/releases/download/latest/ffmpeg-n5.1-latest-win32-gpl-5.1.zip",
|
"386": "https://github.com/sudo-nautilus/FFmpeg-Builds-Win32/releases/download/latest/ffmpeg-n5.1-latest-win32-gpl-5.1.zip",
|
||||||
},
|
},
|
||||||
"darwin": {
|
"darwin": {
|
||||||
"amd64": "https://evermeet.cx/ffmpeg/getrelease/zip",
|
"amd64": "https://evermeet.cx/ffmpeg/getrelease/zip",
|
||||||
"arm64": "https://www.osxexperts.net/FFmpeg511ARM.zip",
|
"arm64": "https://www.osxexperts.net/FFmpeg511ARM.zip",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -220,7 +226,7 @@ func Autoconf(filename string) (*Config, error) {
|
|||||||
if err := os.Remove(filename); err != nil {
|
if err := os.Remove(filename); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cfg.FfmpegPath = "./"+targetFile
|
cfg.FfmpegPath = "./" + targetFile
|
||||||
macosEnableExecutable(cfg.FfmpegPath)
|
macosEnableExecutable(cfg.FfmpegPath)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Using FFmpeg executable found at", cfg.FfmpegPath)
|
fmt.Println("Using FFmpeg executable found at", cfg.FfmpegPath)
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrUnsupportedOSAndArch = errors.New("no download available for your operating system and hardware architecture")
|
ErrUnsupportedOSAndArch = errors.New("no download available for your operating system and hardware architecture")
|
||||||
ErrFileNotFoundInArchive = errors.New("file not found in archive")
|
ErrFileNotFoundInArchive = errors.New("file not found in archive")
|
||||||
ErrUnsupportedArchive = errors.New("unsupported archive format (supported are .tar, .tar.gz, .tar.xz and .zip")
|
ErrUnsupportedArchive = errors.New("unsupported archive format (supported are .tar, .tar.gz, .tar.xz and .zip")
|
||||||
)
|
)
|
||||||
|
|
||||||
func download(executable bool, urlsByOS map[string]map[string]string, progCallback func(progress float32)) (filename string, err error) {
|
func download(executable bool, urlsByOS map[string]map[string]string, progCallback func(progress float32)) (filename string, err error) {
|
||||||
@@ -85,7 +85,7 @@ func download(executable bool, urlsByOS map[string]map[string]string, progCallba
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if progCallback != nil && size != 0 {
|
if progCallback != nil && size != 0 {
|
||||||
progCallback(float32(i)/float32(size))
|
progCallback(float32(i) / float32(size))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return savePath, nil
|
return savePath, nil
|
||||||
|
|||||||
@@ -8,21 +8,21 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrNoSearchResults = errors.New("no search provider available")
|
ErrNoSearchResults = errors.New("no search results")
|
||||||
ErrNoSearchProvider = errors.New("no search provider available")
|
ErrNoSearchProvider = errors.New("no search provider available")
|
||||||
ErrNoSuggestionProvider = errors.New("no search suggestion provider available")
|
ErrNoSuggestionProvider = errors.New("no search suggestion provider available")
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
providers []provider
|
providers []provider
|
||||||
extractors []extractor
|
extractors []extractor
|
||||||
searchers []searcher
|
searchers []searcher
|
||||||
suggestors []suggestor
|
suggestors []suggestor
|
||||||
defaultConfig Config
|
defaultConfig Config
|
||||||
)
|
)
|
||||||
|
|
||||||
func Extract(cfg Config, input string) ([]Data, error) {
|
func Extract(cfg Config, input string) ([]Data, error) {
|
||||||
if err := cfg.CheckTypes(); err != nil {
|
if err := cfg.CheckValidity(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, e := range extractors {
|
for _, e := range extractors {
|
||||||
@@ -45,7 +45,7 @@ func Extract(cfg Config, input string) ([]Data, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Search(cfg Config, input string) ([]Data, error) {
|
func Search(cfg Config, input string) ([]Data, error) {
|
||||||
if err := cfg.CheckTypes(); err != nil {
|
if err := cfg.CheckValidity(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, s := range searchers {
|
for _, s := range searchers {
|
||||||
@@ -59,7 +59,7 @@ func Search(cfg Config, input string) ([]Data, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Suggest(cfg Config, input string) ([]string, error) {
|
func Suggest(cfg Config, input string) ([]string, error) {
|
||||||
if err := cfg.CheckTypes(); err != nil {
|
if err := cfg.CheckValidity(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, s := range suggestors {
|
for _, s := range suggestors {
|
||||||
@@ -115,19 +115,19 @@ func DefaultConfig() Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg Config) CheckTypes() error {
|
func (cfg Config) CheckValidity() error {
|
||||||
for provider, pCfg := range cfg {
|
for chkProvider, chkCfg := range DefaultConfig() {
|
||||||
if pCfg == nil {
|
if _, ok := cfg[chkProvider]; !ok {
|
||||||
return fmt.Errorf("extractor config for %v is nil", provider)
|
return fmt.Errorf("extractor config for %v is nil", chkProvider)
|
||||||
}
|
}
|
||||||
for k, v := range pCfg {
|
for k, v := range chkCfg {
|
||||||
got, expected := reflect.TypeOf(v), reflect.TypeOf(DefaultConfig()[provider][k])
|
expected, got := reflect.TypeOf(v), reflect.TypeOf(cfg[chkProvider][k])
|
||||||
if got != expected {
|
if got != expected {
|
||||||
return &ConfigTypeError{
|
return &ConfigTypeError{
|
||||||
Provider: provider,
|
Provider: chkProvider,
|
||||||
Key: k,
|
Key: k,
|
||||||
Expected: expected,
|
Expected: expected,
|
||||||
Got: got,
|
Got: got,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,9 +137,9 @@ func (cfg Config) CheckTypes() error {
|
|||||||
|
|
||||||
type ConfigTypeError struct {
|
type ConfigTypeError struct {
|
||||||
Provider string
|
Provider string
|
||||||
Key string
|
Key string
|
||||||
Expected reflect.Type
|
Expected reflect.Type
|
||||||
Got reflect.Type
|
Got reflect.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ConfigTypeError) Error() string {
|
func (e *ConfigTypeError) Error() string {
|
||||||
@@ -151,7 +151,7 @@ func (e *ConfigTypeError) Error() string {
|
|||||||
if e.Got != nil {
|
if e.Got != nil {
|
||||||
gotName = e.Got.Name()
|
gotName = e.Got.Name()
|
||||||
}
|
}
|
||||||
return "extractor config type error: "+e.Provider+"."+e.Key+": expected "+expectedName+" but got "+gotName
|
return "invalid extractor configuration: " + e.Provider + "." + e.Key + ": expected " + expectedName + " but got " + gotName
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProviderConfig map[string]any
|
type ProviderConfig map[string]any
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ func getDecryptFunction(baseJs string) (string, error) {
|
|||||||
}
|
}
|
||||||
fn := baseJs[start+len(startMatch):]
|
fn := baseJs[start+len(startMatch):]
|
||||||
end := strings.Index(fn, endMatch)
|
end := strings.Index(fn, endMatch)
|
||||||
if start == -1 {
|
if end == -1 {
|
||||||
return "", ErrDecryptGettingFunction
|
return "", ErrDecryptGettingFunction
|
||||||
}
|
}
|
||||||
return fn[:end], nil
|
return fn[:end], nil
|
||||||
@@ -170,7 +170,7 @@ func getDecryptOps(baseJs string) ([]decryptorOp, error) {
|
|||||||
}
|
}
|
||||||
ops = baseJs[start+len(startMatch):]
|
ops = baseJs[start+len(startMatch):]
|
||||||
end := strings.Index(ops, endMatch)
|
end := strings.Index(ops, endMatch)
|
||||||
if start == -1 {
|
if end == -1 {
|
||||||
return nil, ErrDecryptGettingOpTable
|
return nil, ErrDecryptGettingOpTable
|
||||||
}
|
}
|
||||||
ops = ops[:end]
|
ops = ops[:end]
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ type CmdAddBack []extractor.Data
|
|||||||
type CmdSeek float64 // seconds
|
type CmdSeek float64 // seconds
|
||||||
type CmdSpeed float64 // speed factor
|
type CmdSpeed float64 // speed factor
|
||||||
type CmdPlayFileAndStop struct {
|
type CmdPlayFileAndStop struct {
|
||||||
DoneCh chan<- struct{}
|
DoneCh chan<- struct{}
|
||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
type CmdGetTime chan<- float64
|
type CmdGetTime chan<- float64
|
||||||
type CmdGetQueue chan<- *Queue
|
type CmdGetQueue chan<- *Queue
|
||||||
@@ -522,8 +522,8 @@ func NewClient(excfg extractor.Config, ffmpegPath string, outCh chan<- []byte, c
|
|||||||
refreshStream(getPlaybackTime(), float64(v))
|
refreshStream(getPlaybackTime(), float64(v))
|
||||||
case CmdPlayFileAndStop:
|
case CmdPlayFileAndStop:
|
||||||
cmd := struct {
|
cmd := struct {
|
||||||
DoneCh chan<- struct{}
|
DoneCh chan<- struct{}
|
||||||
Data []byte
|
Data []byte
|
||||||
}(v)
|
}(v)
|
||||||
|
|
||||||
audioch, errch, killch = audio.StreamToDiscordOpus(ffmpegPath, "pipe:", bytes.NewReader(cmd.Data), 0, 1.0, false)
|
audioch, errch, killch = audio.StreamToDiscordOpus(ffmpegPath, "pipe:", bytes.NewReader(cmd.Data), 0, 1.0, false)
|
||||||
|
|||||||
Reference in New Issue
Block a user