refactor: full rewrite
This commit is contained in:
parent
7f7555f05c
commit
8f8dfaf514
16 changed files with 885 additions and 542 deletions
8
Makefile
8
Makefile
|
|
@ -38,19 +38,19 @@ win64: $(WINBIN)
|
|||
|
||||
.PHONY: $(OSXBIN)
|
||||
$(OSXBIN):
|
||||
GO111MODULE=$(GOMOD) GOARCH=$(GOARCH) GOOS=$(GOOSX) CGO_ENABLED=$(CGO_ENABLED) $(CC) $(CFLAGS) -o $(OSXBIN) -ldflags="$(LDFLAGS)" -gcflags="$(GCFLAGS)" -asmflags="$(ASMFLAGS)" ./src
|
||||
GO111MODULE=$(GOMOD) GOARCH=$(GOARCH) GOOS=$(GOOSX) CGO_ENABLED=$(CGO_ENABLED) $(CC) $(CFLAGS) -o $(OSXBIN) -ldflags="$(LDFLAGS)" -gcflags="$(GCFLAGS)" -asmflags="$(ASMFLAGS)" ./cmd/cli
|
||||
|
||||
.PHONY: $(ARMBIN)
|
||||
$(ARMBIN):
|
||||
GO111MODULE=$(GOMOD) GOARCH=$(GOARCH) GOOS=$(GOOSLINUX) CGO_ENABLED=$(CGO_ENABLED) $(CC) $(CFLAGS) -o $(ARMBIN) -ldflags="$(LDFLAGS)" -gcflags="$(GCFLAGS)" -asmflags="$(ASMFLAGS)" ./src
|
||||
GO111MODULE=$(GOMOD) GOARCH=$(GOARCH) GOOS=$(GOOSLINUX) CGO_ENABLED=$(CGO_ENABLED) $(CC) $(CFLAGS) -o $(ARMBIN) -ldflags="$(LDFLAGS)" -gcflags="$(GCFLAGS)" -asmflags="$(ASMFLAGS)" ./cmd/cli
|
||||
|
||||
.PHONY: $(LINUXBIN)
|
||||
$(LINUXBIN):
|
||||
GO111MODULE=$(GOMOD) GOARCH=$(GOARCH) GOOS=$(GOOSLINUX) CGO_ENABLED=$(CGO_ENABLED) $(CC) $(CFLAGS) -o $(LINUXBIN) -ldflags="$(LDFLAGS)" -gcflags="$(GCFLAGS)" -asmflags="$(ASMFLAGS)" ./src
|
||||
GO111MODULE=$(GOMOD) GOARCH=$(GOARCH) GOOS=$(GOOSLINUX) CGO_ENABLED=$(CGO_ENABLED) $(CC) $(CFLAGS) -o $(LINUXBIN) -ldflags="$(LDFLAGS)" -gcflags="$(GCFLAGS)" -asmflags="$(ASMFLAGS)" ./cmd/cli
|
||||
|
||||
.PHONY: $(WINBIN)
|
||||
$(WINBIN):
|
||||
GO111MODULE=$(GOMOD) GOARCH=$(GOARCH) GOOS=$(GOOSWIN) CGO_ENABLED=$(CGO_ENABLED) $(CC) $(CFLAGS) -o $(WINBIN) -ldflags="$(LDFLAGS)" -gcflags="$(GCFLAGS)" -asmflags="$(ASMFLAGS)" ./src
|
||||
GO111MODULE=$(GOMOD) GOARCH=$(GOARCH) GOOS=$(GOOSWIN) CGO_ENABLED=$(CGO_ENABLED) $(CC) $(CFLAGS) -o $(WINBIN) -ldflags="$(LDFLAGS)" -gcflags="$(GCFLAGS)" -asmflags="$(ASMFLAGS)" ./cmd/cli
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
|
|
|
|||
75
api/api.go
Normal file
75
api/api.go
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"gitnet.fr/deblan/mu-go/fs"
|
||||
)
|
||||
|
||||
type ApiError struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func List(e echo.Context, directory, url string) error {
|
||||
files, err := fs.Files(directory, url)
|
||||
if err != nil {
|
||||
return e.JSON(http.StatusInternalServerError, ApiError{Error: fmt.Sprintf("%s", err)})
|
||||
}
|
||||
|
||||
name := e.QueryParam("name")
|
||||
order := e.QueryParam("order")
|
||||
|
||||
if name != "" {
|
||||
name = strings.ToLower(name)
|
||||
var newFiles []fs.File
|
||||
|
||||
for _, file := range files {
|
||||
isMatchingName, _ := regexp.MatchString(name, strings.ToLower(file.Name))
|
||||
|
||||
if isMatchingName {
|
||||
newFiles = append(newFiles, file)
|
||||
}
|
||||
}
|
||||
|
||||
files = newFiles
|
||||
}
|
||||
|
||||
sort.SliceStable(files, func(i, j int) bool {
|
||||
if order == "date" {
|
||||
return files[i].Date < files[j].Date
|
||||
} else if order == "name" {
|
||||
return files[i].Name < files[j].Name
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
return e.JSONPretty(http.StatusOK, files, " ")
|
||||
}
|
||||
|
||||
func Stream(e echo.Context, directory, url string) error {
|
||||
files, err := fs.Files(directory, url)
|
||||
path := e.QueryParam("path")
|
||||
|
||||
if err != nil {
|
||||
return e.JSON(http.StatusInternalServerError, ApiError{Error: fmt.Sprintf("%s", err)})
|
||||
}
|
||||
|
||||
if path == "" {
|
||||
return e.JSON(http.StatusBadRequest, ApiError{Error: "\"path\" query param is missing"})
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if file.RelativePath == path {
|
||||
e.Response().Header().Del("Content-Length")
|
||||
http.ServeFile(e.Response(), e.Request(), file.Path)
|
||||
}
|
||||
}
|
||||
|
||||
return e.JSON(http.StatusNotFound, ApiError{Error: "file not found"})
|
||||
}
|
||||
202
cmd/cli/main.go
Normal file
202
cmd/cli/main.go
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/urfave/cli/v2"
|
||||
"gitnet.fr/deblan/mu-go/internal/shell"
|
||||
"gitnet.fr/deblan/mu-go/internal/web"
|
||||
)
|
||||
|
||||
var (
|
||||
Commands = []*cli.Command{}
|
||||
version = "dev"
|
||||
defaultListen = "127.0.0.1"
|
||||
defaultPort = "4000"
|
||||
defaultApiUrl = "http://127.0.0.1:4000"
|
||||
defaultServerDirectory = "."
|
||||
defaultDownloadDirectory = "."
|
||||
defaultName = ""
|
||||
defaultOrder = "date"
|
||||
)
|
||||
|
||||
var (
|
||||
flagListen = "listen"
|
||||
flagPort = "port"
|
||||
flagApiUrl = "api-url"
|
||||
flagDirectory = "directory"
|
||||
flagDebug = "debug"
|
||||
flagName = "name"
|
||||
flagOrder = "order"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "serve",
|
||||
Aliases: []string{"s"},
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: flagListen,
|
||||
Aliases: []string{"l"},
|
||||
Value: defaultListen,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagPort,
|
||||
Aliases: []string{"p"},
|
||||
Value: defaultPort,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagApiUrl,
|
||||
Aliases: []string{"u"},
|
||||
Value: defaultApiUrl,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagDirectory,
|
||||
Aliases: []string{"d"},
|
||||
Value: defaultServerDirectory,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagDebug,
|
||||
},
|
||||
},
|
||||
Usage: "run http server to serve api and files",
|
||||
Action: func(ctx *cli.Context) error {
|
||||
listen := ctx.String("listen")
|
||||
port := ctx.Int64("port")
|
||||
directory := strings.TrimSuffix(ctx.String(flagDirectory), "/")
|
||||
url := strings.TrimSuffix(ctx.String(flagApiUrl), "/")
|
||||
|
||||
e := echo.New()
|
||||
|
||||
if ctx.Bool(flagDebug) {
|
||||
e.Use(middleware.Logger())
|
||||
e.Use(middleware.Recover())
|
||||
}
|
||||
|
||||
e.GET("/api/list", func(ctx echo.Context) error {
|
||||
return web.List(ctx, directory, url)
|
||||
})
|
||||
|
||||
e.GET("/api/stream", func(ctx echo.Context) error {
|
||||
return web.Stream(ctx, directory, url)
|
||||
})
|
||||
|
||||
e.Logger.Fatal(e.Start(fmt.Sprintf("%s:%d", listen, port)))
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "play",
|
||||
Usage: "run player",
|
||||
Aliases: []string{"p"},
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: flagApiUrl,
|
||||
Aliases: []string{"u"},
|
||||
Value: defaultApiUrl,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagName,
|
||||
Aliases: []string{"n"},
|
||||
Value: defaultName,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagOrder,
|
||||
Aliases: []string{"o"},
|
||||
Value: defaultOrder,
|
||||
},
|
||||
},
|
||||
Action: func(ctx *cli.Context) error {
|
||||
s := shell.NewShell(
|
||||
ctx.String(flagName),
|
||||
ctx.String(flagOrder),
|
||||
ctx.String(flagDirectory),
|
||||
ctx.String(flagApiUrl),
|
||||
)
|
||||
|
||||
return s.Run("play", ctx.Args())
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "download",
|
||||
Usage: "run downloder",
|
||||
Aliases: []string{"d"},
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: flagApiUrl,
|
||||
Aliases: []string{"u"},
|
||||
Value: defaultApiUrl,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagDirectory,
|
||||
Aliases: []string{"d"},
|
||||
Value: defaultDownloadDirectory,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagName,
|
||||
Aliases: []string{"n"},
|
||||
Value: defaultName,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagOrder,
|
||||
Aliases: []string{"o"},
|
||||
Value: defaultOrder,
|
||||
},
|
||||
},
|
||||
Action: func(ctx *cli.Context) error {
|
||||
s := shell.NewShell(
|
||||
ctx.String(flagName),
|
||||
ctx.String(flagOrder),
|
||||
ctx.String(flagDirectory),
|
||||
ctx.String(flagApiUrl),
|
||||
)
|
||||
|
||||
return s.Run("download", ctx.Args())
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// func runCmds(cmds []*exec.Cmd) {
|
||||
// for _, cmd := range cmds {
|
||||
// fmt.Printf("<----->\nCommand: %s\n<----->\n", cmd)
|
||||
//
|
||||
// stdout, _ := cmd.StdoutPipe()
|
||||
// scanner := bufio.NewScanner(stdout)
|
||||
//
|
||||
// err := cmd.Start()
|
||||
// if err != nil {
|
||||
// fmt.Printf("%+v\n", err)
|
||||
// }
|
||||
//
|
||||
// for scanner.Scan() {
|
||||
// out := fmt.Sprintf("%q", scanner.Text())
|
||||
// out = strings.Trim(out, "\"")
|
||||
// out = strings.ReplaceAll(out, `\u00a0`, " ")
|
||||
//
|
||||
// if out != "" {
|
||||
// fmt.Print("\r")
|
||||
// fmt.Print(out)
|
||||
// } else {
|
||||
// fmt.Print("\n")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// for _, cmd := range cmds {
|
||||
// cmd.Wait()
|
||||
// }
|
||||
// }
|
||||
31
go.mod
31
go.mod
|
|
@ -1,6 +1,8 @@
|
|||
module gitnet.fr/deblan/mu-go
|
||||
|
||||
go 1.18
|
||||
go 1.24.0
|
||||
|
||||
toolchain go1.24.6
|
||||
|
||||
require (
|
||||
github.com/h2non/filetype v1.1.3
|
||||
|
|
@ -9,18 +11,43 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/c-bata/go-prompt v0.2.6 // indirect
|
||||
github.com/charmbracelet/bubbles v0.21.0 // indirect
|
||||
github.com/charmbracelet/bubbletea v1.3.9 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||
github.com/charmbracelet/lipgloss v1.1.0 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.10.1 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/labstack/gommon v0.4.1 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mattn/go-tty v0.0.3 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/pkg/term v1.2.0-beta.2 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rodaine/table v1.3.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect
|
||||
golang.org/x/crypto v0.17.0 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/term v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
)
|
||||
|
|
|
|||
84
go.sum
84
go.sum
|
|
@ -1,11 +1,38 @@
|
|||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/c-bata/go-prompt v0.2.6 h1:POP+nrHE+DfLYx370bedwNhsqmpCUynWPxuHi0C5vZI=
|
||||
github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY=
|
||||
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
|
||||
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
|
||||
github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI=
|
||||
github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo=
|
||||
github.com/charmbracelet/bubbletea v1.3.9 h1:OBYdfRo6QnlIcXNmcoI2n1NNS65Nk6kI2L2FO1puS/4=
|
||||
github.com/charmbracelet/bubbletea v1.3.9/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
|
||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
|
||||
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
|
||||
github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
|
||||
github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
|
||||
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||
github.com/labstack/echo/v4 v4.8.0 h1:wdc6yKVaHxkNOEdz4cRZs1pQkwSXPiRjq69yWP4QQS8=
|
||||
|
|
@ -20,10 +47,17 @@ github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8
|
|||
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/labstack/gommon v0.4.1 h1:gqEff0p/hTENGMABzezPoPSRtIh1Cvw0ueMOe0/dfOk=
|
||||
github.com/labstack/gommon v0.4.1/go.mod h1:TyTrpPqxR5KMk8LKVtLmfMjeQ5FEkBYdxLYPw/WfrOM=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
|
|
@ -31,11 +65,41 @@ github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPn
|
|||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.6/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.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI=
|
||||
github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||
github.com/pkg/term v1.2.0-beta.2 h1:L3y/h2jkuBVFdWiJvNfYfKmzcCnILw7mJWm2JQuMppw=
|
||||
github.com/pkg/term v1.2.0-beta.2/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rodaine/table v1.3.0 h1:4/3S3SVkHnVZX91EHFvAMV7K42AnJ0XuymRR2C5HlGE=
|
||||
github.com/rodaine/table v1.3.0/go.mod h1:47zRsHar4zw0jgxGxL9YtFfs7EGN6B/TaS+/Dmk4WxU=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
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.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/urfave/cli/v2 v2.11.2 h1:FVfNg4m3vbjbBpLYxW//WjxUoHvJ9TlppXcqY9Q9ZfA=
|
||||
github.com/urfave/cli/v2 v2.11.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo=
|
||||
github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY=
|
||||
|
|
@ -48,6 +112,8 @@ github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52
|
|||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI=
|
||||
|
|
@ -64,7 +130,18 @@ golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
|
|||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
@ -74,6 +151,12 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
|
|
@ -89,3 +172,4 @@ golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
|||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
5
internal/api/error.go
Normal file
5
internal/api/error.go
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
package api
|
||||
|
||||
type ApiError struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
28
internal/api/remote.go
Normal file
28
internal/api/remote.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"gitnet.fr/deblan/mu-go/internal/fs"
|
||||
)
|
||||
|
||||
func RequestFileList(url, name, order string) fs.Files {
|
||||
response, err := http.Get(fmt.Sprintf("%s/api/list?name=%s&order=%s", url, name, order))
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
|
||||
var files fs.Files
|
||||
|
||||
json.Unmarshal([]byte(body), &files)
|
||||
|
||||
return files
|
||||
}
|
||||
30
internal/cmd/generator.go
Normal file
30
internal/cmd/generator.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
|
||||
"gitnet.fr/deblan/mu-go/internal/fs"
|
||||
)
|
||||
|
||||
func PlayerCmd(files fs.Files) *exec.Cmd {
|
||||
cmd := exec.Command("mpv", "-fs")
|
||||
|
||||
for _, f := range files {
|
||||
cmd.Args = append(cmd.Args, f.Url)
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func DownloadCmds(files fs.Files, directory string) []*exec.Cmd {
|
||||
var cmds []*exec.Cmd
|
||||
|
||||
for _, f := range files {
|
||||
output := fmt.Sprintf("%s/%s", directory, f.Name)
|
||||
cmd := exec.Command("wget", "-o", "/dev/stdout", "-c", "--show-progress", "-O", output, f.Url)
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
|
||||
return cmds
|
||||
}
|
||||
39
internal/fs/file.go
Normal file
39
internal/fs/file.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/h2non/filetype"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
Name string `json:"name"`
|
||||
Url string `json:"url"`
|
||||
Path string `json:"-"`
|
||||
RelativePath string `json:"-"`
|
||||
Head []byte `json:"-"`
|
||||
ContentType string `json:"-"`
|
||||
Date int64 `json:"-"`
|
||||
}
|
||||
|
||||
type Files []File
|
||||
|
||||
func (f Files) Empty() bool {
|
||||
return len(f) == 0
|
||||
}
|
||||
|
||||
func (f *File) GenerateHeadAndContentType() {
|
||||
fo, _ := os.Open(f.Path)
|
||||
head := make([]byte, 261)
|
||||
fo.Read(head)
|
||||
|
||||
f.Head = head
|
||||
f.ContentType = http.DetectContentType(head)
|
||||
|
||||
fo.Close()
|
||||
}
|
||||
|
||||
func (f File) IsSupported() bool {
|
||||
return filetype.IsVideo(f.Head)
|
||||
}
|
||||
118
internal/fs/filter.go
Normal file
118
internal/fs/filter.go
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
func (f Files) FilterByWildcard() Files {
|
||||
var result Files
|
||||
|
||||
for i := len(f) - 1; i >= 0; i-- {
|
||||
result = append(result, f[i])
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (f Files) FilterByRange1(from, to int) Files {
|
||||
var result Files
|
||||
size := len(f)
|
||||
|
||||
if from > to {
|
||||
for i := from; i >= to; i-- {
|
||||
result = append(result, f[size-i])
|
||||
}
|
||||
} else {
|
||||
for i := from; i <= to; i++ {
|
||||
result = append(result, f[size-i])
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (f Files) FilterByRange2(from int) Files {
|
||||
var result Files
|
||||
size := len(f)
|
||||
|
||||
for i := from; i >= 1; i-- {
|
||||
result = append(result, f[size-i])
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (f Files) FilterByRange3(from int) Files {
|
||||
var result Files
|
||||
size := len(f)
|
||||
|
||||
for i := from; i <= size; i++ {
|
||||
result = append(result, f[size-i])
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (f Files) FilterByWords(words []string) Files {
|
||||
var result Files
|
||||
size := len(f)
|
||||
|
||||
for _, word := range words {
|
||||
isInt, _ := regexp.MatchString("^[0-9]+$", word)
|
||||
|
||||
if isInt {
|
||||
value, _ := strconv.Atoi(word)
|
||||
result = append(result, f[size-value])
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (f Files) Select(input string) Files {
|
||||
switch input {
|
||||
case "*":
|
||||
case "*+":
|
||||
return f.FilterByWildcard()
|
||||
case "*-":
|
||||
return f
|
||||
}
|
||||
|
||||
range1Regex := "^([0-9]+)-([0-9]+)$"
|
||||
range2Regex := "^([0-9]+)-$"
|
||||
range3Regex := "^([0-9]+)\\+$"
|
||||
|
||||
isRange1, _ := regexp.MatchString(range1Regex, input)
|
||||
isRange2, _ := regexp.MatchString(range2Regex, input)
|
||||
isRange3, _ := regexp.MatchString(range3Regex, input)
|
||||
|
||||
if isRange1 {
|
||||
regex, _ := regexp.Compile(range1Regex)
|
||||
data := regex.FindStringSubmatch(input)
|
||||
|
||||
return f.FilterByRange1(
|
||||
cast.ToInt(data[1]),
|
||||
cast.ToInt(data[2]),
|
||||
)
|
||||
}
|
||||
|
||||
if isRange2 {
|
||||
regex, _ := regexp.Compile(range2Regex)
|
||||
data := regex.FindStringSubmatch(input)
|
||||
|
||||
return f.FilterByRange2(cast.ToInt(data[1]))
|
||||
}
|
||||
|
||||
if isRange3 {
|
||||
regex, _ := regexp.Compile(range3Regex)
|
||||
data := regex.FindStringSubmatch(input)
|
||||
|
||||
return f.FilterByRange3(cast.ToInt(data[1]))
|
||||
}
|
||||
|
||||
return f.FilterByWords(strings.Fields(input))
|
||||
}
|
||||
54
internal/fs/scanner.go
Normal file
54
internal/fs/scanner.go
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func DirectoryFiles(directory, baseUrl string) (Files, error) {
|
||||
var files Files
|
||||
|
||||
absoluteRootPath, err := filepath.Abs(directory)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
basename := string(info.Name())
|
||||
relativePath := strings.Replace(path, absoluteRootPath, "", 1)
|
||||
|
||||
file := File{
|
||||
Name: basename,
|
||||
Path: path,
|
||||
RelativePath: relativePath,
|
||||
Date: info.ModTime().Unix(),
|
||||
Url: fmt.Sprintf("%s/api/stream?path=%s", baseUrl, url.QueryEscape(relativePath)),
|
||||
}
|
||||
|
||||
file.GenerateHeadAndContentType()
|
||||
|
||||
if file.IsSupported() {
|
||||
files = append(files, file)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
22
internal/render/files.go
Normal file
22
internal/render/files.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package render
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/rodaine/table"
|
||||
"gitnet.fr/deblan/mu-go/internal/fs"
|
||||
)
|
||||
|
||||
func RenderFiles(files fs.Files) {
|
||||
tbl := table.New("ID", "Name")
|
||||
tbl.WithFirstColumnFormatter(color.New(color.FgCyan).SprintfFunc())
|
||||
|
||||
size := len(files)
|
||||
|
||||
for key, file := range files {
|
||||
tbl.AddRow(fmt.Sprintf("%3d", size-key), file.Name)
|
||||
}
|
||||
|
||||
tbl.Print()
|
||||
}
|
||||
112
internal/shell/shell.go
Normal file
112
internal/shell/shell.go
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
package shell
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"gitnet.fr/deblan/mu-go/internal/api"
|
||||
"gitnet.fr/deblan/mu-go/internal/cmd"
|
||||
"gitnet.fr/deblan/mu-go/internal/render"
|
||||
)
|
||||
|
||||
type Shell struct {
|
||||
Name string
|
||||
Order string
|
||||
Directory string
|
||||
ApiUrl string
|
||||
}
|
||||
|
||||
func NewShell(name, order, directory, apiUrl string) *Shell {
|
||||
return &Shell{
|
||||
Name: name,
|
||||
Order: order,
|
||||
Directory: directory,
|
||||
ApiUrl: strings.TrimSuffix(apiUrl, "/"),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Shell) Prompt(defaultValue string) string {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
fmt.Printf("> [%s] ", defaultValue)
|
||||
input, _ := reader.ReadString('\n')
|
||||
input = strings.Replace(input, "\n", "", -1)
|
||||
|
||||
if input == "" {
|
||||
input = defaultValue
|
||||
}
|
||||
|
||||
return input
|
||||
}
|
||||
|
||||
func (s *Shell) Run(action string, args cli.Args) error {
|
||||
directory := strings.TrimSuffix(s.Directory, "/")
|
||||
|
||||
files := api.RequestFileList(
|
||||
s.ApiUrl,
|
||||
s.Name,
|
||||
s.Order,
|
||||
)
|
||||
|
||||
input := strings.Trim(args.Get(0), " ")
|
||||
|
||||
switch input {
|
||||
case "":
|
||||
render.RenderFiles(files)
|
||||
input = s.Prompt("1")
|
||||
case "q":
|
||||
return nil
|
||||
}
|
||||
|
||||
files = files.Select(input)
|
||||
|
||||
if files.Empty() {
|
||||
fmt.Println("Empty list, aborded!")
|
||||
return nil
|
||||
}
|
||||
|
||||
var commands []*exec.Cmd
|
||||
|
||||
switch action {
|
||||
case "play":
|
||||
commands = append(commands, cmd.PlayerCmd(files))
|
||||
case "download":
|
||||
commands = cmd.DownloadCmds(files, directory)
|
||||
}
|
||||
|
||||
s.ExecCommands(commands)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Shell) ExecCommands(commands []*exec.Cmd) {
|
||||
for _, cmd := range commands {
|
||||
fmt.Printf("<----->\nCommand: %s\n<----->\n", cmd)
|
||||
|
||||
stdout, _ := cmd.StdoutPipe()
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
|
||||
cmd.Start()
|
||||
|
||||
for scanner.Scan() {
|
||||
out := fmt.Sprintf("%q", scanner.Text())
|
||||
out = strings.Trim(out, "\"")
|
||||
out = strings.ReplaceAll(out, `\u00a0`, " ")
|
||||
|
||||
if out != "" {
|
||||
fmt.Print("\r")
|
||||
fmt.Print(out)
|
||||
} else {
|
||||
fmt.Print("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, cmd := range commands {
|
||||
cmd.Wait()
|
||||
}
|
||||
}
|
||||
51
internal/web/list.go
Normal file
51
internal/web/list.go
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"gitnet.fr/deblan/mu-go/internal/api"
|
||||
"gitnet.fr/deblan/mu-go/internal/fs"
|
||||
)
|
||||
|
||||
func List(e echo.Context, directory, url string) error {
|
||||
files, err := fs.DirectoryFiles(directory, url)
|
||||
|
||||
if err != nil {
|
||||
return e.JSON(http.StatusInternalServerError, api.ApiError{Error: fmt.Sprintf("%s", err)})
|
||||
}
|
||||
|
||||
name := e.QueryParam("name")
|
||||
order := e.QueryParam("order")
|
||||
|
||||
if name != "" {
|
||||
name = strings.ToLower(name)
|
||||
var newFiles []fs.File
|
||||
|
||||
for _, file := range files {
|
||||
isMatchingName, _ := regexp.MatchString(name, strings.ToLower(file.Name))
|
||||
|
||||
if isMatchingName {
|
||||
newFiles = append(newFiles, file)
|
||||
}
|
||||
}
|
||||
|
||||
files = newFiles
|
||||
}
|
||||
|
||||
sort.SliceStable(files, func(i, j int) bool {
|
||||
if order == "date" {
|
||||
return files[i].Date < files[j].Date
|
||||
} else if order == "name" {
|
||||
return files[i].Name < files[j].Name
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
return e.JSONPretty(http.StatusOK, files, " ")
|
||||
}
|
||||
32
internal/web/stream.go
Normal file
32
internal/web/stream.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"gitnet.fr/deblan/mu-go/internal/api"
|
||||
"gitnet.fr/deblan/mu-go/internal/fs"
|
||||
)
|
||||
|
||||
func Stream(e echo.Context, directory, url string) error {
|
||||
files, err := fs.DirectoryFiles(directory, url)
|
||||
path := e.QueryParam("path")
|
||||
|
||||
if err != nil {
|
||||
return e.JSON(http.StatusInternalServerError, api.ApiError{Error: fmt.Sprintf("%s", err)})
|
||||
}
|
||||
|
||||
if path == "" {
|
||||
return e.JSON(http.StatusBadRequest, api.ApiError{Error: "\"path\" query param is missing"})
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if file.RelativePath == path {
|
||||
e.Response().Header().Del("Content-Length")
|
||||
http.ServeFile(e.Response(), e.Request(), file.Path)
|
||||
}
|
||||
}
|
||||
|
||||
return e.JSON(http.StatusNotFound, api.ApiError{Error: "file not found"})
|
||||
}
|
||||
536
src/main.go
536
src/main.go
|
|
@ -1,536 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/h2non/filetype"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
netUrl "net/url"
|
||||
)
|
||||
|
||||
var (
|
||||
Commands = []*cli.Command{}
|
||||
version = "dev"
|
||||
defaultListen = "127.0.0.1"
|
||||
defaultPort = "4000"
|
||||
defaultApiUrl = "http://127.0.0.1:4000"
|
||||
defaultServerDirectory = "."
|
||||
defaultDownloadDirectory = "."
|
||||
defaultName = ""
|
||||
defaultOrder = "date"
|
||||
)
|
||||
|
||||
var (
|
||||
flagListen = "listen"
|
||||
flagPort = "port"
|
||||
flagApiUrl = "api-url"
|
||||
flagDirectory = "directory"
|
||||
flagDebug = "debug"
|
||||
flagName = "name"
|
||||
flagOrder = "order"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "serve",
|
||||
Aliases: []string{"s"},
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: flagListen,
|
||||
Aliases: []string{"l"},
|
||||
Value: defaultListen,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagPort,
|
||||
Aliases: []string{"p"},
|
||||
Value: defaultPort,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagApiUrl,
|
||||
Aliases: []string{"u"},
|
||||
Value: defaultApiUrl,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagDirectory,
|
||||
Aliases: []string{"d"},
|
||||
Value: defaultServerDirectory,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagDebug,
|
||||
},
|
||||
},
|
||||
Usage: "run http server to serve api and files",
|
||||
Action: func(ctx *cli.Context) error {
|
||||
listen := ctx.String("listen")
|
||||
port := ctx.Int64("port")
|
||||
directory := strings.TrimSuffix(ctx.String(flagDirectory), "/")
|
||||
url := strings.TrimSuffix(ctx.String(flagApiUrl), "/")
|
||||
|
||||
e := echo.New()
|
||||
|
||||
if ctx.Bool(flagDebug) {
|
||||
e.Use(middleware.Logger())
|
||||
e.Use(middleware.Recover())
|
||||
}
|
||||
|
||||
e.GET("/api/list", func(ctx echo.Context) error {
|
||||
return apiList(ctx, directory, url)
|
||||
})
|
||||
|
||||
e.GET("/api/stream", func(ctx echo.Context) error {
|
||||
return apiStream(ctx, directory, url)
|
||||
})
|
||||
|
||||
e.Logger.Fatal(e.Start(fmt.Sprintf("%s:%d", listen, port)))
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "play",
|
||||
Usage: "run player",
|
||||
Aliases: []string{"p"},
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: flagApiUrl,
|
||||
Aliases: []string{"u"},
|
||||
Value: defaultApiUrl,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagName,
|
||||
Aliases: []string{"n"},
|
||||
Value: defaultName,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagOrder,
|
||||
Aliases: []string{"o"},
|
||||
Value: defaultOrder,
|
||||
},
|
||||
},
|
||||
Action: func(ctx *cli.Context) error {
|
||||
return runShell(ctx, "play")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "download",
|
||||
Usage: "run downloder",
|
||||
Aliases: []string{"d"},
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: flagApiUrl,
|
||||
Aliases: []string{"u"},
|
||||
Value: defaultApiUrl,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagDirectory,
|
||||
Aliases: []string{"d"},
|
||||
Value: defaultDownloadDirectory,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagName,
|
||||
Aliases: []string{"n"},
|
||||
Value: defaultName,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagOrder,
|
||||
Aliases: []string{"o"},
|
||||
Value: defaultOrder,
|
||||
},
|
||||
},
|
||||
Action: func(ctx *cli.Context) error {
|
||||
return runShell(ctx, "download")
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
type File struct {
|
||||
Name string `json:"name"`
|
||||
Url string `json:"url"`
|
||||
Path string `json:"-"`
|
||||
RelativePath string `json:"-"`
|
||||
Head []byte `json:"-"`
|
||||
ContentType string `json:"-"`
|
||||
Date int64 `json:"-"`
|
||||
}
|
||||
|
||||
func (f *File) GenerateHeadAndContentType() {
|
||||
fo, _ := os.Open(f.Path)
|
||||
head := make([]byte, 261)
|
||||
fo.Read(head)
|
||||
|
||||
f.Head = head
|
||||
f.ContentType = http.DetectContentType(head)
|
||||
|
||||
fo.Close()
|
||||
}
|
||||
|
||||
func (f File) IsSupported() bool {
|
||||
return filetype.IsVideo(f.Head)
|
||||
}
|
||||
|
||||
type ApiError struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func getFiles(directory, url string) ([]File, error) {
|
||||
files := []File{}
|
||||
absoluteRootPath, err := filepath.Abs(directory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
basename := string(info.Name())
|
||||
relativePath := strings.Replace(path, absoluteRootPath, "", 1)
|
||||
|
||||
file := File{
|
||||
Name: basename,
|
||||
Path: path,
|
||||
RelativePath: relativePath,
|
||||
Date: info.ModTime().Unix(),
|
||||
Url: fmt.Sprintf("%s/api/stream?path=%s", url, netUrl.QueryEscape(relativePath)),
|
||||
}
|
||||
|
||||
file.GenerateHeadAndContentType()
|
||||
|
||||
if file.IsSupported() {
|
||||
files = append(files, file)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func apiStream(e echo.Context, directory, url string) error {
|
||||
files, err := getFiles(directory, url)
|
||||
path := e.QueryParam("path")
|
||||
|
||||
if err != nil {
|
||||
return e.JSON(http.StatusInternalServerError, ApiError{Error: fmt.Sprintf("%s", err)})
|
||||
}
|
||||
|
||||
if path == "" {
|
||||
return e.JSON(http.StatusBadRequest, ApiError{Error: "\"path\" query param is missing"})
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if file.RelativePath == path {
|
||||
e.Response().Header().Del("Content-Length")
|
||||
http.ServeFile(e.Response(), e.Request(), file.Path)
|
||||
}
|
||||
}
|
||||
|
||||
return e.JSON(http.StatusNotFound, ApiError{Error: "file not found"})
|
||||
}
|
||||
|
||||
func apiList(e echo.Context, directory, url string) error {
|
||||
files, err := getFiles(directory, url)
|
||||
if err != nil {
|
||||
return e.JSON(http.StatusInternalServerError, ApiError{Error: fmt.Sprintf("%s", err)})
|
||||
}
|
||||
|
||||
name := e.QueryParam("name")
|
||||
order := e.QueryParam("order")
|
||||
|
||||
if name != "" {
|
||||
name = strings.ToLower(name)
|
||||
var newFiles []File
|
||||
|
||||
for _, file := range files {
|
||||
isMatchingName, _ := regexp.MatchString(name, strings.ToLower(file.Name))
|
||||
|
||||
if isMatchingName {
|
||||
newFiles = append(newFiles, file)
|
||||
}
|
||||
}
|
||||
|
||||
files = newFiles
|
||||
}
|
||||
|
||||
sort.SliceStable(files, func(i, j int) bool {
|
||||
if order == "date" {
|
||||
return files[i].Date < files[j].Date
|
||||
} else if order == "name" {
|
||||
return files[i].Name < files[j].Name
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
return e.JSONPretty(http.StatusOK, files, " ")
|
||||
}
|
||||
|
||||
func showFiles(files []File) {
|
||||
size := len(files)
|
||||
|
||||
for key, file := range files {
|
||||
fmt.Printf("[%3d] %s\n", size-key, file.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func promptInput(defaultValue string) string {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
fmt.Printf("> [%s] ", defaultValue)
|
||||
input, _ := reader.ReadString('\n')
|
||||
input = strings.Replace(input, "\n", "", -1)
|
||||
|
||||
if input == "" {
|
||||
input = defaultValue
|
||||
}
|
||||
|
||||
return input
|
||||
}
|
||||
|
||||
func getFilesByWildcard(files, result []File) []File {
|
||||
for i := len(files) - 1; i >= 0; i-- {
|
||||
result = append(result, files[i])
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func getFilesByRange1(files, result []File, a, b int) []File {
|
||||
size := len(files)
|
||||
|
||||
if a > b {
|
||||
fmt.Printf("%+v\n", a)
|
||||
fmt.Printf("%+v\n", b)
|
||||
for i := a; i >= b; i-- {
|
||||
result = append(result, files[size-i])
|
||||
}
|
||||
|
||||
fmt.Printf("%+v\n", result)
|
||||
} else {
|
||||
for i := a; i <= b; i++ {
|
||||
result = append(result, files[size-i])
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func getFilesByRange2(files, result []File, a int) []File {
|
||||
size := len(files)
|
||||
|
||||
for i := a; i >= 1; i-- {
|
||||
result = append(result, files[size-i])
|
||||
fmt.Println(i)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func getFilesByRange3(files, result []File, a int) []File {
|
||||
size := len(files)
|
||||
|
||||
for i := a; i <= size; i++ {
|
||||
result = append(result, files[size-i])
|
||||
fmt.Println(i)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func getFilesByWordSplit(files, result []File, words []string) []File {
|
||||
size := len(files)
|
||||
|
||||
for _, word := range words {
|
||||
isInt, _ := regexp.MatchString("^[0-9]+$", word)
|
||||
|
||||
if isInt {
|
||||
value, _ := strconv.Atoi(word)
|
||||
result = append(result, files[size-value])
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func requestFileList(url, name, order string) []File {
|
||||
response, err := http.Get(fmt.Sprintf("%s/api/list?name=%s&order=%s", url, name, order))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
|
||||
var files []File
|
||||
|
||||
json.Unmarshal([]byte(body), &files)
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
func generatePlayerCmd(files []File) *exec.Cmd {
|
||||
cmd := exec.Command("mpv", "-fs")
|
||||
|
||||
for _, f := range files {
|
||||
cmd.Args = append(cmd.Args, f.Url)
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func generateDownloadCmds(files []File, directory string) []*exec.Cmd {
|
||||
var cmds []*exec.Cmd
|
||||
|
||||
for _, f := range files {
|
||||
output := fmt.Sprintf("%s/%s", directory, f.Name)
|
||||
cmd := exec.Command("wget", "-o", "/dev/stdout", "-c", "--show-progress", "-O", output, f.Url)
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
|
||||
return cmds
|
||||
}
|
||||
|
||||
func selectFiles(files []File, input string) []File {
|
||||
var result []File
|
||||
|
||||
range1Regex := "^([0-9]+)-([0-9]+)$"
|
||||
range2Regex := "^([0-9]+)-$"
|
||||
range3Regex := "^([0-9]+)\\+$"
|
||||
|
||||
isRange1, _ := regexp.MatchString(range1Regex, input)
|
||||
isRange2, _ := regexp.MatchString(range2Regex, input)
|
||||
isRange3, _ := regexp.MatchString(range3Regex, input)
|
||||
|
||||
if input == "*" || input == "*+" {
|
||||
result = getFilesByWildcard(files, result)
|
||||
} else if input == "*-" {
|
||||
result = files
|
||||
} else if isRange1 { // a-b
|
||||
regex, _ := regexp.Compile(range1Regex)
|
||||
data := regex.FindStringSubmatch(input)
|
||||
|
||||
a, _ := strconv.Atoi(data[1])
|
||||
b, _ := strconv.Atoi(data[2])
|
||||
|
||||
result = getFilesByRange1(files, result, a, b)
|
||||
} else if isRange2 { // a-
|
||||
regex, _ := regexp.Compile(range2Regex)
|
||||
data := regex.FindStringSubmatch(input)
|
||||
|
||||
a, _ := strconv.Atoi(data[1])
|
||||
|
||||
result = getFilesByRange2(files, result, a)
|
||||
} else if isRange3 { // a+
|
||||
regex, _ := regexp.Compile(range3Regex)
|
||||
data := regex.FindStringSubmatch(input)
|
||||
|
||||
a, _ := strconv.Atoi(data[1])
|
||||
|
||||
result = getFilesByRange3(files, result, a)
|
||||
} else {
|
||||
result = getFilesByWordSplit(files, result, strings.Fields(input))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func runCmds(cmds []*exec.Cmd) {
|
||||
for _, cmd := range cmds {
|
||||
fmt.Printf("<----->\nCommand: %s\n<----->\n", cmd)
|
||||
|
||||
stdout, _ := cmd.StdoutPipe()
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
fmt.Printf("%+v\n", err)
|
||||
}
|
||||
|
||||
for scanner.Scan() {
|
||||
out := fmt.Sprintf("%q", scanner.Text())
|
||||
out = strings.Trim(out, "\"")
|
||||
out = strings.ReplaceAll(out, `\u00a0`, " ")
|
||||
|
||||
if out != "" {
|
||||
fmt.Print("\r")
|
||||
fmt.Print(out)
|
||||
} else {
|
||||
fmt.Print("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, cmd := range cmds {
|
||||
cmd.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func runShell(ctx *cli.Context, action string) error {
|
||||
directory := strings.TrimSuffix(ctx.String(flagDirectory), "/")
|
||||
name := ctx.String(flagName)
|
||||
order := ctx.String(flagOrder)
|
||||
files := requestFileList(strings.TrimSuffix(ctx.String(flagApiUrl), "/"), name, order)
|
||||
input := strings.Trim(ctx.Args().Get(0), " ")
|
||||
|
||||
if input == "" {
|
||||
showFiles(files)
|
||||
input = promptInput("1")
|
||||
}
|
||||
|
||||
if input == "q" {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := selectFiles(files, input)
|
||||
|
||||
if len(result) == 0 {
|
||||
fmt.Println("Empty list, aborded!")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var cmds []*exec.Cmd
|
||||
|
||||
if action == "play" {
|
||||
cmd := generatePlayerCmd(result)
|
||||
cmds = append(cmds, cmd)
|
||||
} else {
|
||||
cmds = generateDownloadCmds(result, directory)
|
||||
}
|
||||
|
||||
runCmds(cmds)
|
||||
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue