Compare commits
99 Commits
Author | SHA1 | Date | |
---|---|---|---|
bbd0b48f0b | |||
d91bdea878 | |||
|
b5555f778d | ||
|
2a7ceb342d | ||
4563d03770 | |||
|
a8f2671a44 | ||
9f92321ca5 | |||
|
e8ff2971ca | ||
4a9c315526 | |||
41792410e7 | |||
|
3044be396c | ||
|
4b5237f381 | ||
302f60ac4f | |||
d7de010442 | |||
|
5a90fb228d | ||
|
18e355d36c | ||
f740e96003 | |||
6a6a1ada21 | |||
c2352547c7 | |||
ae9d766246 | |||
|
eb39b4fb05 | ||
|
0d974324ca | ||
|
9ea787f1ae | ||
d8fb4da2a7 | |||
c2a4655cdd | |||
d98a7e82dc | |||
|
7092c3d520 | ||
|
e7e9eedb3b | ||
ad529e7aab | |||
|
43300710ef | ||
6bbcd97b1e | |||
2925b2c432 | |||
4cd781d5db | |||
d4f4b142f7 | |||
2a942a3803 | |||
95c64ebd70 | |||
e39355fb69 | |||
696ee7e263 | |||
84b3bd2f90 | |||
b569bd5bd6 | |||
e05573c2cf | |||
232a1cd3b6 | |||
a34633ec6d | |||
5081c44304 | |||
7e22c90311 | |||
efb03c1aad | |||
b5f76fb321 | |||
6ed3453dae | |||
a386f90ec5 | |||
81d5135d1a | |||
693b228194 | |||
c124ec4eb9 | |||
ec60177128 | |||
8f179f0bc6 | |||
ee9c2299dc | |||
2fbeb6d21a | |||
55f9c73966 | |||
50d5cd18a8 | |||
2b0ea8ed9d | |||
d222bc2e49 | |||
45ea45de40 | |||
a1e9cc6dee | |||
7e65581333 | |||
c930490f34 | |||
9a0cd91fcd | |||
b02764ef33 | |||
fe7f16b9ad | |||
cc05ab7042 | |||
23f85fa1ac | |||
d39ca73b74 | |||
f57e6cecc9 | |||
7ea37f8061 | |||
9625551767 | |||
b6d2988bc4 | |||
5c409f4314 | |||
5e4a8c3799 | |||
14e230985a | |||
e1bc6ea65d | |||
58474e3507 | |||
f12a47c5ca | |||
c3d907e87f | |||
2fd2af1292 | |||
ab4c9d4214 | |||
8a4ac17743 | |||
6abd74ea1c | |||
67e67d32eb | |||
e58674ce17 | |||
a8bfc458ef | |||
bd1fbc8b27 | |||
9feea44400 | |||
b9b8b4e9f0 | |||
7bd2b8c3db | |||
5804607c42 | |||
148b93f0f8 | |||
1fe232cb96 | |||
f1a0e8bb3c | |||
b8c736ee11 | |||
57b8840d3e | |||
74cb9d0872 |
14
.gitignore
vendored
14
.gitignore
vendored
@ -1,3 +1,17 @@
|
|||||||
bin/
|
bin/
|
||||||
bin/*
|
bin/*
|
||||||
/gospt
|
/gospt
|
||||||
|
gospt_zsh
|
||||||
|
gospt_bash
|
||||||
|
gospt_fish
|
||||||
|
completions
|
||||||
|
|
||||||
|
.idea/*
|
||||||
|
.idea
|
||||||
|
|
||||||
|
*.log
|
||||||
|
*.out
|
||||||
|
|
||||||
|
*.tmp
|
||||||
|
|
||||||
|
dist/
|
||||||
|
87
.golangci.yml
Normal file
87
.golangci.yml
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
run:
|
||||||
|
deadline: 10m
|
||||||
|
skip-dirs:
|
||||||
|
- hack
|
||||||
|
|
||||||
|
linters:
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
- gofmt
|
||||||
|
- errcheck
|
||||||
|
- gosimple
|
||||||
|
- govet
|
||||||
|
- ineffassign
|
||||||
|
- staticcheck
|
||||||
|
- gocritic
|
||||||
|
- bodyclose
|
||||||
|
- gosec
|
||||||
|
- prealloc
|
||||||
|
- unconvert
|
||||||
|
- unused
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
gocritic:
|
||||||
|
# Which checks should be enabled; can't be combined with 'disabled-checks';
|
||||||
|
# See https://go-critic.github.io/overview#checks-overview
|
||||||
|
# To check which checks are enabled run `GL_DEBUG=gocritic ./build/bin/golangci-lint run`
|
||||||
|
# By default list of stable checks is used.
|
||||||
|
enabled-checks:
|
||||||
|
- ruleguard
|
||||||
|
- truncateCmp
|
||||||
|
|
||||||
|
# Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty
|
||||||
|
disabled-checks:
|
||||||
|
- captLocal
|
||||||
|
- assignOp
|
||||||
|
- paramTypeCombine
|
||||||
|
- importShadow
|
||||||
|
- commentFormatting
|
||||||
|
- rangeValCopy
|
||||||
|
|
||||||
|
# Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.
|
||||||
|
# Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
|
||||||
|
enabled-tags:
|
||||||
|
- performance
|
||||||
|
- diagnostic
|
||||||
|
- opinionated
|
||||||
|
disabled-tags:
|
||||||
|
- experimental
|
||||||
|
settings:
|
||||||
|
hugeParam:
|
||||||
|
# size in bytes that makes the warning trigger (default 80)
|
||||||
|
sizeThreshold: 1000
|
||||||
|
rangeExprCopy:
|
||||||
|
# size in bytes that makes the warning trigger (default 512)
|
||||||
|
sizeThreshold: 512
|
||||||
|
# whether to check test functions (default true)
|
||||||
|
skipTestFuncs: true
|
||||||
|
truncateCmp:
|
||||||
|
# whether to skip int/uint/uintptr types (default true)
|
||||||
|
skipArchDependent: true
|
||||||
|
underef:
|
||||||
|
# whether to skip (*x).method() calls where x is a pointer receiver (default true)
|
||||||
|
skipRecvDeref: true
|
||||||
|
|
||||||
|
govet:
|
||||||
|
disable:
|
||||||
|
- deepequalerrors
|
||||||
|
- fieldalignment
|
||||||
|
- shadow
|
||||||
|
- unsafeptr
|
||||||
|
goconst:
|
||||||
|
min-len: 2
|
||||||
|
min-occurrences: 2
|
||||||
|
gofmt:
|
||||||
|
auto-fix: false
|
||||||
|
|
||||||
|
issues:
|
||||||
|
exclude-rules:
|
||||||
|
- linters:
|
||||||
|
- golint
|
||||||
|
text: "should be"
|
||||||
|
- linters:
|
||||||
|
- errcheck
|
||||||
|
text: "not checked"
|
||||||
|
- linters:
|
||||||
|
- staticcheck
|
||||||
|
text: "SA(1019|1029|5011)"
|
61
.goreleaser.yaml
Normal file
61
.goreleaser.yaml
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
gitea_urls:
|
||||||
|
api: https://git.asdf.cafe/api/v1
|
||||||
|
download: https://git.asdf.cafe
|
||||||
|
skip_tls_verify: false
|
||||||
|
|
||||||
|
before:
|
||||||
|
hooks:
|
||||||
|
- go mod tidy
|
||||||
|
|
||||||
|
builds:
|
||||||
|
- env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
goos:
|
||||||
|
- linux
|
||||||
|
- windows
|
||||||
|
- darwin
|
||||||
|
ignore:
|
||||||
|
- goos: windows
|
||||||
|
goarch: "386"
|
||||||
|
ldflags:
|
||||||
|
- -s -w -X git.asdf.cafe/abs3nt/gospt/src.cmd.Version={{.Version}}
|
||||||
|
|
||||||
|
archives:
|
||||||
|
- format: tar.gz
|
||||||
|
name_template: >-
|
||||||
|
{{ .ProjectName }}_
|
||||||
|
{{- title .Os }}_
|
||||||
|
{{- if eq .Arch "amd64" }}x86_64
|
||||||
|
{{- else if eq .Arch "386" }}i386
|
||||||
|
{{- else }}{{ .Arch }}{{ end }}
|
||||||
|
{{- if .Arm }}v{{ .Arm }}{{ end }}
|
||||||
|
format_overrides:
|
||||||
|
- goos: windows
|
||||||
|
format: zip
|
||||||
|
files:
|
||||||
|
- completions/*
|
||||||
|
rlcp: true
|
||||||
|
checksum:
|
||||||
|
name_template: 'checksums.txt'
|
||||||
|
snapshot:
|
||||||
|
name_template: "{{ incpatch .Version }}-next"
|
||||||
|
changelog:
|
||||||
|
sort: asc
|
||||||
|
groups:
|
||||||
|
- title: Added
|
||||||
|
regexp: '^.*?ADD(\([[:word:]]+\))??!?:.+$'
|
||||||
|
order: 0
|
||||||
|
- title: 'Bug fixes'
|
||||||
|
regexp: '^.*?BUG(\([[:word:]]+\))??!?:.+$'
|
||||||
|
order: 1
|
||||||
|
- title: 'Enhancements'
|
||||||
|
regexp: '^.*?IMPROVED(\([[:word:]]+\))??!?:.+$'
|
||||||
|
order: 1
|
||||||
|
- title: 'Docs'
|
||||||
|
regexp: '^.*?DOC(\([[:word:]]+\))??!?:.+$'
|
||||||
|
order: 1
|
||||||
|
- title: 'CI'
|
||||||
|
regexp: '^.*?CI(\([[:word:]]+\))??!?:.+$'
|
||||||
|
order: 1
|
||||||
|
- title: Others
|
||||||
|
order: 999
|
@ -1,24 +1,18 @@
|
|||||||
pipeline:
|
steps:
|
||||||
build:
|
build:
|
||||||
image: golang:1.19
|
image: golang:1.22
|
||||||
commands:
|
commands:
|
||||||
|
- go mod tidy
|
||||||
- go build -o gospt
|
- go build -o gospt
|
||||||
- ./gospt completion zsh > gospt_zsh
|
- mkdir completions
|
||||||
- ./gospt completion bash > gospt_bash
|
- ./gospt completion zsh > completions/gospt_zsh
|
||||||
- ./gospt completion fish > gospt_fish
|
- ./gospt completion bash > completions/gospt_bash
|
||||||
|
- ./gospt completion fish > completions/gospt_fish
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
image: woodpeckerci/plugin-gitea-release
|
image: goreleaser/goreleaser
|
||||||
settings:
|
commands:
|
||||||
skip_verify: true
|
- goreleaser release --clean
|
||||||
base_url: https://gitea.asdf.cafe
|
secrets: [ gitea_token ]
|
||||||
files:
|
|
||||||
- gospt
|
|
||||||
- gospt_zsh
|
|
||||||
- gospt_bash
|
|
||||||
- gospt_fish
|
|
||||||
api_key:
|
|
||||||
from_secret: GITEA_KEY
|
|
||||||
checksum: sha256
|
|
||||||
when:
|
when:
|
||||||
event: tag
|
event: tag
|
||||||
|
20
README.md
20
README.md
@ -1,7 +1,7 @@
|
|||||||
IF YOU ARE ON GITHUB.COM GO HERE INSTEAD: https://gitea.asdf.cafe/abs3nt/gospt :)
|
IF YOU ARE ON GITHUB.COM GO HERE INSTEAD: https://git.asdf.cafe/abs3nt/gospt :)
|
||||||
|
|
||||||
|
|
||||||
If you open an issue or PR on github I won't see it please use gitea. Register on asdf and open your PRs there
|
If you open an issue or PR on github I won't see it please use git. Register on asdf and open your PRs there
|
||||||
|
|
||||||
This project is still under heavy development and some things might not work or not work as intended. Don't hesitate to open an issue to let me know.
|
This project is still under heavy development and some things might not work or not work as intended. Don't hesitate to open an issue to let me know.
|
||||||
|
|
||||||
@ -12,21 +12,27 @@ This project is still under heavy development and some things might not work or
|
|||||||
# To install (with a package manager):
|
# To install (with a package manager):
|
||||||
|
|
||||||
## Archlinux ([AUR])
|
## Archlinux ([AUR])
|
||||||
```yay -S gospt```
|
```yay -S gospt```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```yay -S gospt-git```
|
||||||
|
|
||||||
## NetBSD ([Official repositories])
|
## NetBSD ([Official repositories])
|
||||||
```pkgin install gospt```
|
```pkgin install gospt```
|
||||||
|
|
||||||
# To build from source by pulling and building the binary
|
# To build from source by pulling and building the binary
|
||||||
|
|
||||||
```git clone https://gitea.asdf.cafe/abs3nt/gospt```
|
|
||||||
|
```git clone https://git.asdf.cafe/abs3nt/gospt```
|
||||||
|
|
||||||
```cd gospt```
|
```cd gospt```
|
||||||
|
|
||||||
|
|
||||||
```make build && sudo make install```
|
```make build && sudo make install```
|
||||||
|
|
||||||
[AUR]: https://aur.archlinux.org/packages/gospt
|
[AUR]: https://aur.archlinux.org/packages/gospt
|
||||||
[Official repositories]: https://pkgsrc.se/audio/gospt/
|
[Official repositories]: http://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/audio/gospt/
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
go here https://developer.spotify.com/dashboard/applications to make a spotify application. you will need a client ID and a client secret. Set your redirect uri like this:
|
go here https://developer.spotify.com/dashboard/applications to make a spotify application. you will need a client ID and a client secret. Set your redirect uri like this:
|
||||||
@ -72,6 +78,6 @@ To view help:
|
|||||||
|
|
||||||
Very open to contributations feel free to open a PR
|
Very open to contributations feel free to open a PR
|
||||||
|
|
||||||
[tmux plugin](https://gitea.asdf.cafe/abs3nt/tmux-gospt)
|
[tmux plugin](https://git.asdf.cafe/abs3nt/tmux-gospt)
|
||||||
|
|
||||||
[wiki](https://gitea.asdf.cafe/abs3nt/gospt/wiki)
|
[wiki](https://git.asdf.cafe/abs3nt/gospt/wiki)
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,6 +13,6 @@ var clearRadioCmd = &cobra.Command{
|
|||||||
Short: "Wipes the radio playlist and creates an empty one",
|
Short: "Wipes the radio playlist and creates an empty one",
|
||||||
Long: `Wipes the radio playlist and creates an empty one, mostly for debugging or if something goes wrong`,
|
Long: `Wipes the radio playlist and creates an empty one, mostly for debugging or if something goes wrong`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
commands.ClearRadio(ctx, client)
|
commands.ClearRadio(ctx)
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,6 +13,6 @@ var devicesCmd = &cobra.Command{
|
|||||||
Short: "Prints out devices",
|
Short: "Prints out devices",
|
||||||
Long: `Prints out devices`,
|
Long: `Prints out devices`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
commands.Devices(ctx, client)
|
commands.Devices(ctx)
|
||||||
},
|
},
|
||||||
}
|
}
|
20
cmd/download_cover.go
Normal file
20
cmd/download_cover.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(downloadCoverCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var downloadCoverCmd = &cobra.Command{
|
||||||
|
Use: "download_cover",
|
||||||
|
Aliases: []string{"dl"},
|
||||||
|
Short: "Returns url for currently playing song art",
|
||||||
|
Long: `Returns url for currently playing song art`,
|
||||||
|
Args: cobra.MatchAll(cobra.ExactArgs(1)),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
commands.DownloadCover(ctx, args)
|
||||||
|
},
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,6 +14,6 @@ var likeCmd = &cobra.Command{
|
|||||||
Short: "Likes song",
|
Short: "Likes song",
|
||||||
Long: `Likes song`,
|
Long: `Likes song`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
commands.Like(ctx, client)
|
commands.Like(ctx)
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -4,8 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,12 +14,12 @@ var linkCmd = &cobra.Command{
|
|||||||
Short: "Print link to currently playing song",
|
Short: "Print link to currently playing song",
|
||||||
Long: `Print link to currently playing song`,
|
Long: `Print link to currently playing song`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
link, err := commands.Link(ctx, client)
|
link, err := commands.Link(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
fmt.Print(link)
|
fmt.Println(link)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -4,8 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,7 +14,7 @@ var linkContextCmd = &cobra.Command{
|
|||||||
Short: "Get url to current context(album, playlist)",
|
Short: "Get url to current context(album, playlist)",
|
||||||
Long: `Get url to current context(album, playlist)`,
|
Long: `Get url to current context(album, playlist)`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
link, err := commands.LinkContext(ctx, client)
|
link, err := commands.LinkContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
@ -1,8 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,7 +13,7 @@ var muteCmd = &cobra.Command{
|
|||||||
Short: "mutes playback",
|
Short: "mutes playback",
|
||||||
Long: `Mutes the spotify device, playback will continue`,
|
Long: `Mutes the spotify device, playback will continue`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
err := commands.SetVolume(ctx, client, 0)
|
err := commands.SetVolume(ctx, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
@ -3,8 +3,6 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,6 +25,6 @@ var nextCmd = &cobra.Command{
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return commands.Next(ctx, client, skipAmt)
|
return commands.Next(ctx, skipAmt, false)
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,7 +13,8 @@ var nowPlayingCmd = &cobra.Command{
|
|||||||
Aliases: []string{"now"},
|
Aliases: []string{"now"},
|
||||||
Short: "Shows song and artist of currently playing song",
|
Short: "Shows song and artist of currently playing song",
|
||||||
Long: `Shows song and artist of currently playing song, useful for scripting`,
|
Long: `Shows song and artist of currently playing song, useful for scripting`,
|
||||||
|
Args: cobra.MatchAll(cobra.RangeArgs(0, 1)),
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
commands.NowPlaying(ctx, client)
|
commands.NowPlaying(ctx, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,6 +14,6 @@ var pauseCmd = &cobra.Command{
|
|||||||
Aliases: []string{"pa"},
|
Aliases: []string{"pa"},
|
||||||
Long: `Pauses currently playing song on spotify`,
|
Long: `Pauses currently playing song on spotify`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
commands.Pause(ctx, client)
|
commands.Pause(ctx)
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,6 +14,6 @@ var playCmd = &cobra.Command{
|
|||||||
Short: "Plays spotify",
|
Short: "Plays spotify",
|
||||||
Long: `Plays queued song on spotify, uses last used device and activates it if needed`,
|
Long: `Plays queued song on spotify, uses last used device and activates it if needed`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
commands.Play(ctx, client)
|
commands.Play(ctx)
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,6 +14,6 @@ var playurlCmd = &cobra.Command{
|
|||||||
Args: cobra.MatchAll(cobra.ExactArgs(1)),
|
Args: cobra.MatchAll(cobra.ExactArgs(1)),
|
||||||
Long: `Plays song from provided url`,
|
Long: `Plays song from provided url`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
commands.PlayUrl(ctx, client, args)
|
commands.PlayUrl(ctx, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,6 +14,6 @@ var previousCmd = &cobra.Command{
|
|||||||
Short: "goes to previous song",
|
Short: "goes to previous song",
|
||||||
Long: `if song is playing it will start over, if close to begining of song it will go to previous song`,
|
Long: `if song is playing it will start over, if close to begining of song it will go to previous song`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
commands.Previous(ctx, client)
|
commands.Previous(ctx)
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,6 +14,6 @@ var radioCmd = &cobra.Command{
|
|||||||
Short: "Starts radio",
|
Short: "Starts radio",
|
||||||
Long: `Starts radio`,
|
Long: `Starts radio`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return commands.Radio(ctx, client)
|
return commands.Radio(ctx)
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,6 +14,6 @@ var refillRadioCmd = &cobra.Command{
|
|||||||
Short: "Refills the radio",
|
Short: "Refills the radio",
|
||||||
Long: `Deletes all songs up to your position in the radio and adds that many songs to the end of the radio`,
|
Long: `Deletes all songs up to your position in the radio and adds that many songs to the end of the radio`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return commands.RefillRadio(ctx, client)
|
return commands.RefillRadio(ctx)
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,6 +13,6 @@ var repeatCmd = &cobra.Command{
|
|||||||
Short: "Toggles repeat",
|
Short: "Toggles repeat",
|
||||||
Long: `Switches between repeating your current context or not, spotifyd does not support single track loops`,
|
Long: `Switches between repeating your current context or not, spotifyd does not support single track loops`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
commands.Repeat(ctx, client)
|
commands.Repeat(ctx)
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -8,22 +8,23 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gospt/src/auth"
|
cmds "git.asdf.cafe/abs3nt/gospt/src/commands"
|
||||||
"gospt/src/config"
|
|
||||||
"gospt/src/gctx"
|
"git.asdf.cafe/abs3nt/gospt/src/config"
|
||||||
|
"git.asdf.cafe/abs3nt/gospt/src/gctx"
|
||||||
|
"tuxpa.in/a/zlog"
|
||||||
|
|
||||||
"github.com/cristalhq/aconfig"
|
"github.com/cristalhq/aconfig"
|
||||||
"github.com/cristalhq/aconfig/aconfigyaml"
|
"github.com/cristalhq/aconfig/aconfigyaml"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/zmb3/spotify/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Used for flags.
|
// Used for flags.
|
||||||
ctx *gctx.Context
|
ctx *gctx.Context
|
||||||
client *spotify.Client
|
commands *cmds.Commands
|
||||||
cfgFile string
|
cfgFile string
|
||||||
userLicense string
|
verbose bool
|
||||||
|
|
||||||
rootCmd = &cobra.Command{
|
rootCmd = &cobra.Command{
|
||||||
Use: "gospt",
|
Use: "gospt",
|
||||||
@ -45,27 +46,21 @@ func Execute(defCmd string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
zlog.SetGlobalLevel(zlog.DebugLevel)
|
||||||
if len(os.Args) > 1 {
|
if len(os.Args) > 1 {
|
||||||
if os.Args[1] == "completion" || os.Args[1] == "__complete" {
|
if os.Args[1] == "completion" || os.Args[1] == "__complete" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cobra.OnInitialize(initConfig)
|
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "enable verbose logging")
|
||||||
cobra.OnInitialize(initClient)
|
cobra.OnInitialize(func() {
|
||||||
}
|
if verbose {
|
||||||
|
zlog.SetGlobalLevel(zlog.TraceLevel)
|
||||||
func initClient() {
|
}
|
||||||
var err error
|
})
|
||||||
ctx = gctx.NewContext(context.Background())
|
ctx = gctx.NewContext(context.Background())
|
||||||
client, err = auth.GetClient(ctx)
|
commands = &cmds.Commands{Context: ctx}
|
||||||
if err != nil {
|
cobra.OnInitialize(initConfig)
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
currentUser, err := client.CurrentUser(ctx)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
ctx.UserId = currentUser.ID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initConfig() {
|
func initConfig() {
|
||||||
@ -94,7 +89,12 @@ func initConfig() {
|
|||||||
}
|
}
|
||||||
if config.Values.ClientSecretCmd != "" {
|
if config.Values.ClientSecretCmd != "" {
|
||||||
args := strings.Fields(config.Values.ClientSecretCmd)
|
args := strings.Fields(config.Values.ClientSecretCmd)
|
||||||
secret, err := exec.Command(args[0], args[1:]...).Output()
|
cmd := args[0]
|
||||||
|
secret_command := exec.Command(cmd)
|
||||||
|
if len(args) > 1 {
|
||||||
|
secret_command.Args = args
|
||||||
|
}
|
||||||
|
secret, err := secret_command.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
@ -3,8 +3,6 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,7 +18,7 @@ var seekCmd = &cobra.Command{
|
|||||||
Long: `Seeks forward or backward, or seeks to a given position in seconds`,
|
Long: `Seeks forward or backward, or seeks to a given position in seconds`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if args[0] == "forward" || args[0] == "f" {
|
if args[0] == "forward" || args[0] == "f" {
|
||||||
err := commands.Seek(ctx, client, true)
|
err := commands.Seek(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -28,7 +26,7 @@ var seekCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if args[0] == "backward" || args[0] == "b" {
|
if args[0] == "backward" || args[0] == "b" {
|
||||||
err := commands.Seek(ctx, client, false)
|
err := commands.Seek(ctx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -40,7 +38,7 @@ var seekCmd = &cobra.Command{
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pos = pos * 1000
|
pos = pos * 1000
|
||||||
err = commands.SetPosition(ctx, client, pos)
|
err = commands.SetPosition(ctx, pos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/tui"
|
"git.asdf.cafe/abs3nt/gospt/src/tui"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@ -15,6 +15,6 @@ var setDeviceCmd = &cobra.Command{
|
|||||||
Short: "Shows tui to pick active device",
|
Short: "Shows tui to pick active device",
|
||||||
Long: `Allows setting or changing the active spotify device, shown in a tui`,
|
Long: `Allows setting or changing the active spotify device, shown in a tui`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
tui.StartTea(ctx, client, "devices")
|
tui.StartTea(ctx, commands, "devices")
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,6 +13,6 @@ var shuffleCmd = &cobra.Command{
|
|||||||
Short: "Toggles shuffle",
|
Short: "Toggles shuffle",
|
||||||
Long: `Enables shuffle if it is currently disabled or disables it if it is currently active`,
|
Long: `Enables shuffle if it is currently disabled or disables it if it is currently active`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
commands.Shuffle(ctx, client)
|
commands.Shuffle(ctx)
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,6 +13,6 @@ var statusCmd = &cobra.Command{
|
|||||||
Short: "Returns player status in json",
|
Short: "Returns player status in json",
|
||||||
Long: `Returns all player status in json, useful for scripting`,
|
Long: `Returns all player status in json, useful for scripting`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
commands.Status(ctx, client)
|
commands.Status(ctx)
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,6 +14,6 @@ var togglePlayCmd = &cobra.Command{
|
|||||||
Short: "Toggles the play state of spotify",
|
Short: "Toggles the play state of spotify",
|
||||||
Long: `If you are playing a song it will pause and if a song is paused it will play`,
|
Long: `If you are playing a song it will pause and if a song is paused it will play`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
commands.TogglePlay(ctx, client)
|
commands.TogglePlay(ctx)
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -4,8 +4,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"gospt/src/commands"
|
"git.asdf.cafe/abs3nt/gospt/src/tui"
|
||||||
"gospt/src/tui"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@ -20,12 +19,12 @@ var tracksCmd = &cobra.Command{
|
|||||||
Long: `Uses TUI to open a list of saved tracks`,
|
Long: `Uses TUI to open a list of saved tracks`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
configDir, _ := os.UserConfigDir()
|
configDir, _ := os.UserConfigDir()
|
||||||
if commands.ActiveDeviceExists(ctx, client) {
|
if commands.ActiveDeviceExists(ctx) {
|
||||||
return tui.StartTea(ctx, client, "tracks")
|
return tui.StartTea(ctx, commands, "tracks")
|
||||||
}
|
}
|
||||||
if _, err := os.Stat(filepath.Join(configDir, "gospt/device.json")); err != nil {
|
if _, err := os.Stat(filepath.Join(configDir, "gospt/device.json")); err != nil {
|
||||||
return tui.StartTea(ctx, client, "devices")
|
return tui.StartTea(ctx, commands, "devices")
|
||||||
}
|
}
|
||||||
return tui.StartTea(ctx, client, "tracks")
|
return tui.StartTea(ctx, commands, "tracks")
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -4,8 +4,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"gospt/src/commands"
|
"git.asdf.cafe/abs3nt/gospt/src/tui"
|
||||||
"gospt/src/tui"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@ -20,12 +19,12 @@ var tuiCmd = &cobra.Command{
|
|||||||
Long: `Default command. this is what will run if no other commands are present. Shows the main menu.`,
|
Long: `Default command. this is what will run if no other commands are present. Shows the main menu.`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
configDir, _ := os.UserConfigDir()
|
configDir, _ := os.UserConfigDir()
|
||||||
if commands.ActiveDeviceExists(ctx, client) {
|
if commands.ActiveDeviceExists(ctx) {
|
||||||
return tui.StartTea(ctx, client, "main")
|
return tui.StartTea(ctx, commands, "main")
|
||||||
}
|
}
|
||||||
if _, err := os.Stat(filepath.Join(configDir, "gospt/device.json")); err != nil {
|
if _, err := os.Stat(filepath.Join(configDir, "gospt/device.json")); err != nil {
|
||||||
return tui.StartTea(ctx, client, "devices")
|
return tui.StartTea(ctx, commands, "devices")
|
||||||
}
|
}
|
||||||
return tui.StartTea(ctx, client, "main")
|
return tui.StartTea(ctx, commands, "main")
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,6 +14,6 @@ var unlikeCmd = &cobra.Command{
|
|||||||
Short: "unlikes song",
|
Short: "unlikes song",
|
||||||
Long: `unlikes song`,
|
Long: `unlikes song`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
commands.Unlike(ctx, client)
|
commands.Unlike(ctx)
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,7 +13,7 @@ var unmuteCmd = &cobra.Command{
|
|||||||
Short: "unmutes playback",
|
Short: "unmutes playback",
|
||||||
Long: `unmutes the spotify device, playback will continue`,
|
Long: `unmutes the spotify device, playback will continue`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
err := commands.SetVolume(ctx, client, 100)
|
err := commands.SetVolume(ctx, 100)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
23
cmd/version.go
Normal file
23
cmd/version.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Version = "v0.0.47"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(versionCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var versionCmd = &cobra.Command{
|
||||||
|
Use: "version",
|
||||||
|
Short: "Prints current verison",
|
||||||
|
Run: version,
|
||||||
|
}
|
||||||
|
|
||||||
|
func version(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Printf("Gospt: %s\n", Version)
|
||||||
|
}
|
@ -3,8 +3,6 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,7 +18,7 @@ var volumeCmd = &cobra.Command{
|
|||||||
Long: `Sets the volume to the given percent [0-100] or increases/decreases by 5 percent if you say up or down`,
|
Long: `Sets the volume to the given percent [0-100] or increases/decreases by 5 percent if you say up or down`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if args[0] == "up" {
|
if args[0] == "up" {
|
||||||
err := commands.ChangeVolume(ctx, client, 5)
|
err := commands.ChangeVolume(ctx, 5)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -28,7 +26,7 @@ var volumeCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if args[0] == "down" {
|
if args[0] == "down" {
|
||||||
err := commands.ChangeVolume(ctx, client, -5)
|
err := commands.ChangeVolume(ctx, -5)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -39,7 +37,7 @@ var volumeCmd = &cobra.Command{
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = commands.SetVolume(ctx, client, vol)
|
err = commands.SetVolume(ctx, vol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
28
cmd/youtube-link.go
Normal file
28
cmd/youtube-link.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// youtubeLinkCmd represents the youtube-link command
|
||||||
|
var youtubeLinkCmd = &cobra.Command{
|
||||||
|
Use: "youtube-link",
|
||||||
|
Aliases: []string{"yl"},
|
||||||
|
Short: "Print youtube link to currently playing song",
|
||||||
|
Long: `Print youtube link to currently playing song`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
link, err := commands.YoutubeLink(ctx)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Print(link)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(youtubeLinkCmd)
|
||||||
|
}
|
90
go.mod
90
go.mod
@ -1,41 +1,83 @@
|
|||||||
module gospt
|
module git.asdf.cafe/abs3nt/gospt
|
||||||
|
|
||||||
go 1.19
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/atotto/clipboard v0.1.4
|
github.com/atotto/clipboard v0.1.4
|
||||||
github.com/charmbracelet/bubbles v0.14.0
|
github.com/charmbracelet/bubbles v0.18.0
|
||||||
github.com/charmbracelet/bubbletea v0.23.1
|
github.com/charmbracelet/bubbletea v0.26.6
|
||||||
github.com/charmbracelet/lipgloss v0.6.0
|
github.com/charmbracelet/lipgloss v0.12.1
|
||||||
github.com/cristalhq/aconfig v0.18.3
|
github.com/cristalhq/aconfig v0.18.5
|
||||||
github.com/cristalhq/aconfig/aconfigyaml v0.17.1
|
github.com/cristalhq/aconfig/aconfigyaml v0.17.1
|
||||||
github.com/spf13/cobra v1.6.1
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/zmb3/spotify/v2 v2.3.1
|
github.com/zmb3/spotify/v2 v2.4.2
|
||||||
golang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5
|
golang.org/x/net v0.27.0
|
||||||
|
golang.org/x/oauth2 v0.21.0
|
||||||
|
golang.org/x/sync v0.7.0
|
||||||
|
google.golang.org/api v0.188.0
|
||||||
|
modernc.org/sqlite v1.30.2
|
||||||
|
tuxpa.in/a/zlog v1.61.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aymanbagabas/go-osc52 v1.0.3 // indirect
|
cloud.google.com/go/auth v0.7.0 // indirect
|
||||||
|
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
|
||||||
|
cloud.google.com/go/compute v1.25.1 // indirect
|
||||||
|
cloud.google.com/go/compute/metadata v0.4.0 // indirect
|
||||||
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/charmbracelet/harmonica v0.2.0 // indirect
|
github.com/charmbracelet/harmonica v0.2.0 // indirect
|
||||||
github.com/containerd/console v1.0.3 // indirect
|
github.com/charmbracelet/x/ansi v0.1.4 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/charmbracelet/x/input v0.1.0 // indirect
|
||||||
|
github.com/charmbracelet/x/term v0.1.1 // indirect
|
||||||
|
github.com/charmbracelet/x/windows v0.1.0 // indirect
|
||||||
|
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.1 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
|
github.com/google/s2a-go v0.1.7 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||||
|
github.com/googleapis/gax-go/v2 v2.12.5 // indirect
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.16 // 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-localereader v0.0.1 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||||
github.com/muesli/reflow v0.3.0 // indirect
|
github.com/muesli/reflow v0.3.0 // indirect
|
||||||
github.com/muesli/termenv v0.13.0 // indirect
|
github.com/muesli/termenv v0.15.2 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
github.com/sahilm/fuzzy v0.1.0 // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
|
github.com/rs/zerolog v1.31.0 // indirect
|
||||||
|
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||||
google.golang.org/protobuf v1.27.1 // indirect
|
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||||
|
golang.org/x/crypto v0.25.0 // indirect
|
||||||
|
golang.org/x/sys v0.22.0 // indirect
|
||||||
|
golang.org/x/term v0.22.0 // indirect
|
||||||
|
golang.org/x/text v0.16.0 // indirect
|
||||||
|
google.golang.org/appengine v1.6.8 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b // indirect
|
||||||
|
google.golang.org/grpc v1.64.1 // indirect
|
||||||
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||||
|
modernc.org/libc v1.52.1 // indirect
|
||||||
|
modernc.org/mathutil v1.6.0 // indirect
|
||||||
|
modernc.org/memory v1.8.0 // indirect
|
||||||
|
modernc.org/strutil v1.2.0 // indirect
|
||||||
|
modernc.org/token v1.1.0 // indirect
|
||||||
)
|
)
|
||||||
|
333
go.sum
333
go.sum
@ -13,12 +13,29 @@ 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.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14=
|
||||||
|
cloud.google.com/go/auth v0.6.1 h1:T0Zw1XM5c1GlpN2HYr2s+m3vr1p2wy+8VN+Z1FKxW38=
|
||||||
|
cloud.google.com/go/auth v0.6.1/go.mod h1:eFHG7zDzbXHKmjJddFG/rBlcGp6t25SwRUiEQSlO4x4=
|
||||||
|
cloud.google.com/go/auth v0.7.0 h1:kf/x9B3WTbBUHkC+1VS8wwwli9TzhSt0vSTVBmMR8Ts=
|
||||||
|
cloud.google.com/go/auth v0.7.0/go.mod h1:D+WqdrpcjmiCgWrXmLLxOVq1GACoE36chW6KXoEvuIw=
|
||||||
|
cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
|
||||||
|
cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
|
||||||
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.23.1 h1:V97tBoDaZHb6leicZ1G6DLK2BAaZLJ/7+9BB/En3hR0=
|
||||||
|
cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78=
|
||||||
|
cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU=
|
||||||
|
cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls=
|
||||||
|
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||||
|
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||||
|
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||||
|
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||||
|
cloud.google.com/go/compute/metadata v0.4.0 h1:vHzJCWaM4g8XIcm8kopr3XmDA4Gy/lblD3EhhSux05c=
|
||||||
|
cloud.google.com/go/compute/metadata v0.4.0/go.mod h1:SIQh1Kkb4ZJ8zJ874fqVkslA29PRXuleyj6vOzlbK7M=
|
||||||
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/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
@ -35,45 +52,82 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
|||||||
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/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||||
github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||||
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/charmbracelet/bubbles v0.14.0 h1:DJfCwnARfWjZLvMglhSQzo76UZ2gucuHPy9jLWX45Og=
|
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
|
||||||
github.com/charmbracelet/bubbles v0.14.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc=
|
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
|
||||||
github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4=
|
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
|
||||||
github.com/charmbracelet/bubbletea v0.23.1 h1:CYdteX1wCiCzKNUlwm25ZHBIc1GXlYFyUIte8WPvhck=
|
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
|
||||||
github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU=
|
github.com/charmbracelet/bubbletea v0.26.6 h1:zTCWSuST+3yZYZnVSvbXwKOPRSNZceVeqpzOLN2zq1s=
|
||||||
|
github.com/charmbracelet/bubbletea v0.26.6/go.mod h1:dz8CWPlfCCGLFbBlTY4N7bjLiyOGDJEnd2Muu7pOWhk=
|
||||||
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
|
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
|
||||||
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
|
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
|
||||||
github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs=
|
github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
|
||||||
github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY=
|
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
|
||||||
github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk=
|
github.com/charmbracelet/lipgloss v0.11.0 h1:UoAcbQ6Qml8hDwSWs0Y1cB5TEQuZkDPH/ZqwWWYTG4g=
|
||||||
|
github.com/charmbracelet/lipgloss v0.11.0/go.mod h1:1UdRTH9gYgpcdNN5oBtjbu/IzNKtzVtb7sqN1t9LNn8=
|
||||||
|
github.com/charmbracelet/lipgloss v0.11.1 h1:a8KgVPHa7kOoP95vm2tQQrjD2AKhbWmfr4uJ2RW6kNk=
|
||||||
|
github.com/charmbracelet/lipgloss v0.11.1/go.mod h1:beLlcmkF7MWA+5UrKKIRo/VJ21xGXr7YJ9miWfdMRIU=
|
||||||
|
github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/NR7A0zQcs=
|
||||||
|
github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8=
|
||||||
|
github.com/charmbracelet/x/ansi v0.1.1 h1:CGAduulr6egay/YVbGc8Hsu8deMg1xZ/bkaXTPi1JDk=
|
||||||
|
github.com/charmbracelet/x/ansi v0.1.1/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
||||||
|
github.com/charmbracelet/x/ansi v0.1.2 h1:6+LR39uG8DE6zAmbu023YlqjJHkYXDF1z36ZwzO4xZY=
|
||||||
|
github.com/charmbracelet/x/ansi v0.1.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
||||||
|
github.com/charmbracelet/x/ansi v0.1.3 h1:RBh/eleNWML5R524mjUF0yVRePTwqN9tPtV+DPgO5Lw=
|
||||||
|
github.com/charmbracelet/x/ansi v0.1.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
||||||
|
github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM=
|
||||||
|
github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
||||||
|
github.com/charmbracelet/x/input v0.1.0 h1:TEsGSfZYQyOtp+STIjyBq6tpRaorH0qpwZUj8DavAhQ=
|
||||||
|
github.com/charmbracelet/x/input v0.1.0/go.mod h1:ZZwaBxPF7IG8gWWzPUVqHEtWhc1+HXJPNuerJGRGZ28=
|
||||||
|
github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI=
|
||||||
|
github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw=
|
||||||
|
github.com/charmbracelet/x/windows v0.1.0 h1:gTaxdvzDM5oMa/I2ZNF7wN78X/atWemG9Wph7Ika2k4=
|
||||||
|
github.com/charmbracelet/x/windows v0.1.0/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ=
|
||||||
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/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/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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
|
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
|
||||||
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/cristalhq/aconfig v0.17.0/go.mod h1:NXaRp+1e6bkO4dJn+wZ71xyaihMDYPtCSvEhMTm/H3E=
|
github.com/cristalhq/aconfig v0.17.0/go.mod h1:NXaRp+1e6bkO4dJn+wZ71xyaihMDYPtCSvEhMTm/H3E=
|
||||||
github.com/cristalhq/aconfig v0.18.3 h1:Or12LIWIF+2mQpcGWA2PQnNc55+WiHFAqRjYh/pQNtM=
|
github.com/cristalhq/aconfig v0.18.5 h1:QqXH/Gy2c4QUQJTV2BN8UAuL/rqZ3IwhvxeC8OgzquA=
|
||||||
github.com/cristalhq/aconfig v0.18.3/go.mod h1:NXaRp+1e6bkO4dJn+wZ71xyaihMDYPtCSvEhMTm/H3E=
|
github.com/cristalhq/aconfig v0.18.5/go.mod h1:NXaRp+1e6bkO4dJn+wZ71xyaihMDYPtCSvEhMTm/H3E=
|
||||||
github.com/cristalhq/aconfig/aconfigyaml v0.17.1 h1:xCCbRKVmKrft9gQj3gHOq6U5PduasvlXEIsxtyzmFZ0=
|
github.com/cristalhq/aconfig/aconfigyaml v0.17.1 h1:xCCbRKVmKrft9gQj3gHOq6U5PduasvlXEIsxtyzmFZ0=
|
||||||
github.com/cristalhq/aconfig/aconfigyaml v0.17.1/go.mod h1:5DTsjHkvQ6hfbyxfG32roB1lF0U82rROtFaLxibL8V8=
|
github.com/cristalhq/aconfig/aconfigyaml v0.17.1/go.mod h1:5DTsjHkvQ6hfbyxfG32roB1lF0U82rROtFaLxibL8V8=
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
|
||||||
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
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=
|
||||||
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/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/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
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-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||||
|
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
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=
|
||||||
@ -94,9 +148,13 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
|
|||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
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.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.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||||
|
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
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=
|
||||||
@ -106,8 +164,10 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.1/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.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.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
github.com/google/go-cmp v0.5.3/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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/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/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
@ -117,13 +177,29 @@ 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-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
|
||||||
|
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||||
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/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||||
|
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||||
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
||||||
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.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBYGmXdxA=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
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/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
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=
|
||||||
@ -138,61 +214,99 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
|
|||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
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/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
|
github.com/mattn/go-colorable v0.1.12/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.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
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-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
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 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
|
||||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
|
|
||||||
github.com/muesli/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
|
||||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
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/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||||
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ=
|
|
||||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||||
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
|
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||||
github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
|
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||||
github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0=
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc=
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
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/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg=
|
||||||
|
github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
|
github.com/rivo/uniseg v0.4.7/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/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
|
github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
|
||||||
|
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
|
||||||
|
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
|
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y=
|
||||||
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||||
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/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.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
|
||||||
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.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
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/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/zmb3/spotify/v2 v2.3.1 h1:aEyIPotROM3JJjHMCImFROgnPIUpzVo8wymYSaPSd9w=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/zmb3/spotify/v2 v2.3.1/go.mod h1:+LVh9CafHu7SedyqYmEf12Rd01dIVlEL845yNhksW0E=
|
github.com/zmb3/spotify/v2 v2.4.2 h1:j3yNN5lKVEMZQItJF4MHCSZbfNWmXO+KaC+3RFaLlLc=
|
||||||
|
github.com/zmb3/spotify/v2 v2.4.2/go.mod h1:XOV7BrThayFYB9AAfB+L0Q0wyxBuLCARk4fI/ZXCBW8=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
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.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||||
|
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
||||||
|
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||||
|
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||||
|
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||||
|
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||||
|
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||||
|
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||||
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
|
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||||
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
|
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||||
|
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||||
|
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||||
|
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||||
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=
|
||||||
@ -223,6 +337,10 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
|||||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
||||||
|
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
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=
|
||||||
@ -249,15 +367,28 @@ 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-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
|
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||||
|
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||||
|
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||||
|
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||||
|
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||||
|
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||||
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=
|
||||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5 h1:Ati8dO7+U7mxpkPSxBZQEvzHVUYB/MqCklCN8ig5w/o=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
|
||||||
|
golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
|
||||||
|
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||||
|
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
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=
|
||||||
@ -266,6 +397,12 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||||
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||||
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
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=
|
||||||
@ -291,26 +428,53 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/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-20220209214540-3681064d5158/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-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
|
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-20220915200043-7b5979e65e41/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||||
|
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||||
|
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||||
|
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
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 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
|
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||||
|
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||||
|
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
|
||||||
|
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||||
|
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||||
|
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||||
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=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
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.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
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=
|
||||||
@ -354,10 +518,13 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
|
|||||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
|
||||||
|
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
|
||||||
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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
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=
|
||||||
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=
|
||||||
@ -375,14 +542,21 @@ 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.148.0 h1:HBq4TZlN4/1pNcu0geJZ/Q50vIwIXT532UIMYoo0vOs=
|
||||||
|
google.golang.org/api v0.148.0/go.mod h1:8/TBgwaKjfqTdacOJrOv2+2Q6fBDU1uHKK06oGSkxzU=
|
||||||
|
google.golang.org/api v0.187.0 h1:Mxs7VATVC2v7CY+7Xwm4ndkX71hpElcvx0D1Ji/p1eo=
|
||||||
|
google.golang.org/api v0.187.0/go.mod h1:KIHlTc4x7N7gKKuVsdmfBXN13yEEWXWFURWY6SBp2gk=
|
||||||
|
google.golang.org/api v0.188.0 h1:51y8fJ/b1AaaBRJr4yWm96fPcuxSo0JcegXE3DaHQHw=
|
||||||
|
google.golang.org/api v0.188.0/go.mod h1:VR0d+2SIiWOYG3r/jdm7adPW9hI2aRv9ETOSCQ9Beag=
|
||||||
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=
|
||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
|
||||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||||
|
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
@ -412,6 +586,16 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
|
|||||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/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-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-20231012201019-e917dd12ba7a h1:fwgW9j3vHirt4ObdHoYNwuO24BEZjSzbh+zPaNWoiY8=
|
||||||
|
google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:EMfReVxb80Dq1hhioy0sOsY9jCE46YDgHlJ7fWVUWRE=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 h1:W18sezcAYs+3tDZX4F80yctqa12jcP1PUS2gQu1zTPU=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d h1:k3zyW3BYYR30e8v3x0bTDdE9vpYFjZHK+HcyqkrppWk=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b h1:04+jVzTs2XBnOZcPsLnmrTGqltqJbZQ1Ey26hjYdQQ0=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||||
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=
|
||||||
@ -424,6 +608,13 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
|
|||||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||||
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.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
|
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
||||||
|
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
||||||
|
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
||||||
|
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
||||||
|
google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
|
||||||
|
google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
|
||||||
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=
|
||||||
@ -436,8 +627,10 @@ 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.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
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=
|
||||||
@ -453,6 +646,36 @@ 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=
|
||||||
|
modernc.org/cc/v4 v4.21.2 h1:dycHFB/jDc3IyacKipCNSDrjIC0Lm1hyoWOZTRR20Lk=
|
||||||
|
modernc.org/cc/v4 v4.21.2/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
|
||||||
|
modernc.org/ccgo/v4 v4.17.10 h1:6wrtRozgrhCxieCeJh85QsxkX/2FFrT9hdaWPlbn4Zo=
|
||||||
|
modernc.org/ccgo/v4 v4.17.10/go.mod h1:0NBHgsqTTpm9cA5z2ccErvGZmtntSM9qD2kFAs6pjXM=
|
||||||
|
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||||
|
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||||
|
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
|
||||||
|
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
|
||||||
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
||||||
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||||
|
modernc.org/libc v1.52.1 h1:uau0VoiT5hnR+SpoWekCKbLqm7v6dhRL3hI+NQhgN3M=
|
||||||
|
modernc.org/libc v1.52.1/go.mod h1:HR4nVzFDSDizP620zcMCgjb1/8xk2lg5p/8yjfGv1IQ=
|
||||||
|
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||||
|
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||||
|
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
|
||||||
|
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
|
||||||
|
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||||
|
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||||
|
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||||
|
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
|
||||||
|
modernc.org/sqlite v1.30.1 h1:YFhPVfu2iIgUf9kuA1CR7iiHdcEEsI2i+yjRYHscyxk=
|
||||||
|
modernc.org/sqlite v1.30.1/go.mod h1:DUmsiWQDaAvU4abhc/N+djlom/L2o8f7gZ95RCvyoLU=
|
||||||
|
modernc.org/sqlite v1.30.2 h1:IPVVkhLu5mMVnS1dQgh3h0SAACRWcVk7aoLP9Us3UCk=
|
||||||
|
modernc.org/sqlite v1.30.2/go.mod h1:DUmsiWQDaAvU4abhc/N+djlom/L2o8f7gZ95RCvyoLU=
|
||||||
|
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||||
|
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||||
|
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||||
|
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
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=
|
||||||
|
tuxpa.in/a/zlog v1.61.0 h1:7wrS6G4QwpnOmgHRQknrr7IgiMXrfGpekkU0PjM9FhE=
|
||||||
|
tuxpa.in/a/zlog v1.61.0/go.mod h1:CNpMe8laDHLSypx/DyxfX1S0oyxUydeo3aGTEbtRBhg=
|
||||||
|
2
main.go
2
main.go
@ -1,7 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gospt/src/cmd"
|
"git.asdf.cafe/abs3nt/gospt/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
3
renovate.json
Normal file
3
renovate.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": ["config:recommended"]
|
||||||
|
}
|
@ -1,17 +1,19 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gospt/src/config"
|
"tuxpa.in/a/zlog/log"
|
||||||
"gospt/src/gctx"
|
|
||||||
|
"git.asdf.cafe/abs3nt/gospt/src/config"
|
||||||
|
"git.asdf.cafe/abs3nt/gospt/src/gctx"
|
||||||
|
|
||||||
"github.com/zmb3/spotify/v2"
|
"github.com/zmb3/spotify/v2"
|
||||||
spotifyauth "github.com/zmb3/spotify/v2/auth"
|
spotifyauth "github.com/zmb3/spotify/v2/auth"
|
||||||
@ -25,6 +27,12 @@ var (
|
|||||||
configDir, _ = os.UserConfigDir()
|
configDir, _ = os.UserConfigDir()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type roundTripperFunc func(*http.Request) (*http.Response, error)
|
||||||
|
|
||||||
|
func (fn roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
return fn(req)
|
||||||
|
}
|
||||||
|
|
||||||
func GetClient(ctx *gctx.Context) (*spotify.Client, error) {
|
func GetClient(ctx *gctx.Context) (*spotify.Client, error) {
|
||||||
if config.Values.ClientId == "" || config.Values.ClientSecret == "" || config.Values.Port == "" {
|
if config.Values.ClientId == "" || config.Values.ClientSecret == "" || config.Values.Port == "" {
|
||||||
fmt.Println("PLEASE WRITE YOUR CONFIG FILE IN", filepath.Join(configDir, "gospt/client.yml"))
|
fmt.Println("PLEASE WRITE YOUR CONFIG FILE IN", filepath.Join(configDir, "gospt/client.yml"))
|
||||||
@ -57,42 +65,36 @@ func GetClient(ctx *gctx.Context) (*spotify.Client, error) {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
if _, err := os.Stat(filepath.Join(configDir, "gospt/auth.json")); err == nil {
|
if _, err := os.Stat(filepath.Join(configDir, "gospt/auth.json")); err == nil {
|
||||||
authFile, err := os.Open(filepath.Join(configDir, "gospt/auth.json"))
|
authFilePath := filepath.Join(configDir, "gospt/auth.json")
|
||||||
|
authFile, err := os.Open(authFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer authFile.Close()
|
defer authFile.Close()
|
||||||
authValue, err := io.ReadAll(authFile)
|
tok := &oauth2.Token{}
|
||||||
|
err = json.NewDecoder(authFile).Decode(tok)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var tok *oauth2.Token
|
ctx.Context = context.WithValue(ctx.Context, oauth2.HTTPClient, &http.Client{
|
||||||
err = json.Unmarshal(authValue, &tok)
|
Transport: roundTripperFunc(func(r *http.Request) (*http.Response, error) {
|
||||||
if err != nil {
|
log.Trace().Interface("path", r.URL.Path).Msg("request")
|
||||||
return nil, err
|
return http.DefaultTransport.RoundTrip(r)
|
||||||
}
|
}),
|
||||||
client := spotify.New(auth.Client(ctx, tok))
|
})
|
||||||
|
authClient := auth.Client(ctx, tok)
|
||||||
|
client := spotify.New(authClient)
|
||||||
new_token, err := client.Token()
|
new_token, err := client.Token()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if new_token != tok {
|
out, err := json.MarshalIndent(new_token, "", " ")
|
||||||
out, err := json.MarshalIndent(new_token, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
|
||||||
err = os.WriteFile(filepath.Join(configDir, "gospt/auth.json"), out, 0o644)
|
|
||||||
if err != nil {
|
|
||||||
panic("FAILED TO SAVE AUTH")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out, err := json.MarshalIndent(tok, "", " ")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err.Error())
|
return nil, err
|
||||||
}
|
}
|
||||||
err = os.WriteFile(filepath.Join(configDir, "gospt/auth.json"), out, 0o644)
|
err = os.WriteFile(authFilePath, out, 0o600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("FAILED TO SAVE AUTH")
|
return nil, fmt.Errorf("failed to save auth")
|
||||||
}
|
}
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
@ -101,11 +103,12 @@ func GetClient(ctx *gctx.Context) (*spotify.Client, error) {
|
|||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Println("Got request for:", r.URL.String())
|
log.Println("Got request for:", r.URL.String())
|
||||||
})
|
})
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: fmt.Sprintf(":%s", config.Values.Port),
|
||||||
|
ReadHeaderTimeout: 5 * time.Second,
|
||||||
|
}
|
||||||
go func() {
|
go func() {
|
||||||
err := http.ListenAndServe(fmt.Sprintf(":%s", config.Values.Port), nil)
|
server.ListenAndServe()
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
url := auth.AuthURL(state)
|
url := auth.AuthURL(state)
|
||||||
fmt.Println(url)
|
fmt.Println(url)
|
||||||
@ -114,6 +117,7 @@ func GetClient(ctx *gctx.Context) (*spotify.Client, error) {
|
|||||||
// wait for auth to complete
|
// wait for auth to complete
|
||||||
client := <-ch
|
client := <-ch
|
||||||
|
|
||||||
|
server.Shutdown(ctx)
|
||||||
// use the client to make calls that require authorization
|
// use the client to make calls that require authorization
|
||||||
user, err := client.CurrentUser(ctx)
|
user, err := client.CurrentUser(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -127,7 +131,6 @@ func completeAuth(w http.ResponseWriter, r *http.Request) {
|
|||||||
tok, err := auth.Token(r.Context(), state, r)
|
tok, err := auth.Token(r.Context(), state, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Couldn't get token", http.StatusForbidden)
|
http.Error(w, "Couldn't get token", http.StatusForbidden)
|
||||||
log.Fatal(err)
|
|
||||||
}
|
}
|
||||||
if st := r.FormValue("state"); st != state {
|
if st := r.FormValue("state"); st != state {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
@ -137,7 +140,7 @@ func completeAuth(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
err = os.WriteFile(filepath.Join(configDir, "gospt/auth.json"), out, 0o644)
|
err = os.WriteFile(filepath.Join(configDir, "gospt/auth.json"), out, 0o600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("FAILED TO SAVE AUTH")
|
panic("FAILED TO SAVE AUTH")
|
||||||
}
|
}
|
||||||
|
98
src/cache/cache.go
vendored
Normal file
98
src/cache/cache.go
vendored
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tuxpa.in/a/zlog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cache struct {
|
||||||
|
Root string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CacheEntry struct {
|
||||||
|
Expire time.Time `json:"e"`
|
||||||
|
Value string `json:"v"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultCache() *Cache {
|
||||||
|
return &Cache{
|
||||||
|
Root: filepath.Join(os.TempDir(), "gospt.cache"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) load() (map[string]CacheEntry, error) {
|
||||||
|
out := map[string]CacheEntry{}
|
||||||
|
cache, err := os.Open(c.Root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(cache).Decode(&out); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) save(m map[string]CacheEntry) error {
|
||||||
|
payload, err := json.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Trace().Str("tosave", string(payload)).Msg("saving cache")
|
||||||
|
err = os.WriteFile(c.Root, payload, 0o600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) GetOrDo(key string, do func() (string, error), ttl time.Duration) (string, error) {
|
||||||
|
conf, err := c.load()
|
||||||
|
if err != nil {
|
||||||
|
log.Trace().Err(err).Msg("cache failed read")
|
||||||
|
return c.Do(key, do, ttl)
|
||||||
|
}
|
||||||
|
val, ok := conf[key]
|
||||||
|
if !ok {
|
||||||
|
return c.Do(key, do, ttl)
|
||||||
|
}
|
||||||
|
if time.Now().After(val.Expire) {
|
||||||
|
return c.Do(key, do, ttl)
|
||||||
|
}
|
||||||
|
return val.Value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Do(key string, do func() (string, error), ttl time.Duration) (string, error) {
|
||||||
|
if do == nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
res, err := do()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return c.Put(key, res, ttl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Put(key string, value string, ttl time.Duration) (string, error) {
|
||||||
|
conf, err := c.load()
|
||||||
|
if err != nil {
|
||||||
|
conf = map[string]CacheEntry{}
|
||||||
|
}
|
||||||
|
conf[key] = CacheEntry{
|
||||||
|
Expire: time.Now().Add(ttl),
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
log.Trace().Str("key", key).Str("val", value).Msg("saving new cache key")
|
||||||
|
err = c.save(conf)
|
||||||
|
if err != nil {
|
||||||
|
log.Trace().Err(err).Msg("cache failed save")
|
||||||
|
}
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Clear() error {
|
||||||
|
return os.Remove(c.Root)
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -2,23 +2,32 @@ package gctx
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"tuxpa.in/a/zlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
|
zlog.Logger
|
||||||
|
Debug zlog.Logger
|
||||||
|
|
||||||
context.Context
|
context.Context
|
||||||
*log.Logger
|
}
|
||||||
Debug *log.Logger
|
|
||||||
UserId string
|
func (c *Context) Err() error {
|
||||||
|
return c.Context.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Println(args ...any) {
|
||||||
|
c.Info().Msg(fmt.Sprint(args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContext(ctx context.Context) *Context {
|
func NewContext(ctx context.Context) *Context {
|
||||||
out := &Context{
|
out := &Context{
|
||||||
ctx,
|
Context: ctx,
|
||||||
log.New(os.Stdout, "LOG:", 0),
|
Logger: zlog.New(os.Stderr),
|
||||||
log.New(os.Stdout, "DEBUG:", 1),
|
Debug: zlog.New(os.Stderr),
|
||||||
"",
|
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
@ -1,109 +1,121 @@
|
|||||||
package tui
|
package tui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"gospt/src/commands"
|
|
||||||
"gospt/src/gctx"
|
|
||||||
|
|
||||||
"github.com/zmb3/spotify/v2"
|
"github.com/zmb3/spotify/v2"
|
||||||
|
|
||||||
|
"git.asdf.cafe/abs3nt/gospt/src/commands"
|
||||||
|
"git.asdf.cafe/abs3nt/gospt/src/gctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandlePlayWithContext(ctx *gctx.Context, client *spotify.Client, uri *spotify.URI, pos int) {
|
func HandlePlayWithContext(ctx *gctx.Context, commands *commands.Commands, uri *spotify.URI, pos *int) {
|
||||||
var err error
|
err := commands.PlaySongInPlaylist(ctx, uri, pos)
|
||||||
err = commands.PlaySongInPlaylist(ctx, client, uri, pos)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleRadio(ctx *gctx.Context, client *spotify.Client, id spotify.ID) {
|
func HandleRadio(ctx *gctx.Context, commands *commands.Commands, song spotify.SimpleTrack) {
|
||||||
err := commands.RadioGivenSong(ctx, client, id, 0)
|
err := commands.RadioGivenSong(ctx, song, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleAlbumRadio(ctx *gctx.Context, client *spotify.Client, id spotify.ID) {
|
func HandleAlbumRadio(ctx *gctx.Context, commands *commands.Commands, album spotify.SimpleAlbum) {
|
||||||
err := commands.RadioFromAlbum(ctx, client, id)
|
err := commands.RadioFromAlbum(ctx, album)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleSeek(ctx *gctx.Context, client *spotify.Client, fwd bool) {
|
func HandleSeek(ctx *gctx.Context, commands *commands.Commands, fwd bool) {
|
||||||
err := commands.Seek(ctx, client, fwd)
|
err := commands.Seek(ctx, fwd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleVolume(ctx *gctx.Context, client *spotify.Client, up bool) {
|
func HandleVolume(ctx *gctx.Context, commands *commands.Commands, up bool) {
|
||||||
vol := 10
|
vol := 10
|
||||||
if !up {
|
if !up {
|
||||||
vol = -10
|
vol = -10
|
||||||
}
|
}
|
||||||
err := commands.ChangeVolume(ctx, client, vol)
|
err := commands.ChangeVolume(ctx, vol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleArtistRadio(ctx *gctx.Context, client *spotify.Client, id spotify.ID) {
|
func HandleArtistRadio(ctx *gctx.Context, commands *commands.Commands, artist spotify.SimpleArtist) {
|
||||||
err := commands.RadioGivenArtist(ctx, client, id)
|
err := commands.RadioGivenArtist(ctx, artist)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleAlbumArtist(ctx *gctx.Context, client *spotify.Client, id spotify.ID) {
|
func HandleAlbumArtist(ctx *gctx.Context, commands *commands.Commands, artist spotify.SimpleArtist) {
|
||||||
err := commands.RadioGivenArtist(ctx, client, id)
|
err := commands.RadioGivenArtist(ctx, artist)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandlePlaylistRadio(ctx *gctx.Context, client *spotify.Client, playlist spotify.SimplePlaylist) {
|
func HandlePlaylistRadio(ctx *gctx.Context, commands *commands.Commands, playlist spotify.SimplePlaylist) {
|
||||||
err := commands.RadioFromPlaylist(ctx, client, playlist)
|
err := commands.RadioFromPlaylist(ctx, playlist)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleLibraryRadio(ctx *gctx.Context, client *spotify.Client) {
|
func HandleLibraryRadio(ctx *gctx.Context, commands *commands.Commands) {
|
||||||
err := commands.RadioFromSavedTracks(ctx, client)
|
err := commands.RadioFromSavedTracks(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandlePlayLikedSong(ctx *gctx.Context, client *spotify.Client, position int) {
|
func HandlePlayLikedSong(ctx *gctx.Context, commands *commands.Commands, position int) {
|
||||||
err := commands.PlayLikedSongs(ctx, client, position)
|
err := commands.PlayLikedSongs(ctx, position)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandlePlayTrack(ctx *gctx.Context, client *spotify.Client, track spotify.ID) {
|
func HandlePlayTrack(ctx *gctx.Context, commands *commands.Commands, track spotify.ID) {
|
||||||
err := commands.QueueSong(ctx, client, track)
|
err := commands.QueueSong(ctx, track)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = commands.Next(ctx, client, 1)
|
err = commands.Next(ctx, 1, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleSetDevice(ctx *gctx.Context, client *spotify.Client, player spotify.PlayerDevice) {
|
func HandleNextInQueue(ctx *gctx.Context, commands *commands.Commands, amt int) {
|
||||||
var err error
|
err := commands.Next(ctx, amt, true)
|
||||||
err = commands.SetDevice(ctx, client, player)
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleQueueItem(ctx *gctx.Context, commands *commands.Commands, item spotify.ID) {
|
||||||
|
err := commands.QueueSong(ctx, item)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleDeleteTrackFromPlaylist(ctx *gctx.Context, commands *commands.Commands, item, playlist spotify.ID) {
|
||||||
|
err := commands.DeleteTracksFromPlaylist(ctx, []spotify.ID{item}, playlist)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleSetDevice(ctx *gctx.Context, commands *commands.Commands, player spotify.PlayerDevice) {
|
||||||
|
err := commands.SetDevice(ctx, player)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,16 +4,18 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gospt/src/commands"
|
|
||||||
|
|
||||||
"github.com/charmbracelet/bubbles/list"
|
"github.com/charmbracelet/bubbles/list"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *mainModel) LoadMoreItems() {
|
func (m *mainModel) LoadMoreItems() {
|
||||||
|
loading = true
|
||||||
|
defer func() {
|
||||||
|
page++
|
||||||
|
loading = false
|
||||||
|
}()
|
||||||
switch m.mode {
|
switch m.mode {
|
||||||
case "artist":
|
case "artist":
|
||||||
albums, err := commands.ArtistAlbums(m.ctx, m.client, m.artist.ID, (page + 1))
|
albums, err := m.commands.ArtistAlbums(m.ctx, m.artist.ID, (page + 1))
|
||||||
page++
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -32,8 +34,7 @@ func (m *mainModel) LoadMoreItems() {
|
|||||||
main_updates <- m
|
main_updates <- m
|
||||||
return
|
return
|
||||||
case "artists":
|
case "artists":
|
||||||
artists, err := commands.UserArtists(m.ctx, m.client, (page + 1))
|
artists, err := m.commands.UserArtists(m.ctx, (page + 1))
|
||||||
page++
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -52,8 +53,7 @@ func (m *mainModel) LoadMoreItems() {
|
|||||||
main_updates <- m
|
main_updates <- m
|
||||||
return
|
return
|
||||||
case "album":
|
case "album":
|
||||||
tracks, err := commands.AlbumTracks(m.ctx, m.client, m.album.ID, (page + 1))
|
tracks, err := m.commands.AlbumTracks(m.ctx, m.album.ID, (page + 1))
|
||||||
page++
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -73,8 +73,7 @@ func (m *mainModel) LoadMoreItems() {
|
|||||||
main_updates <- m
|
main_updates <- m
|
||||||
return
|
return
|
||||||
case "albums":
|
case "albums":
|
||||||
albums, err := commands.UserAlbums(m.ctx, m.client, (page + 1))
|
albums, err := m.commands.UserAlbums(m.ctx, (page + 1))
|
||||||
page++
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -93,8 +92,7 @@ func (m *mainModel) LoadMoreItems() {
|
|||||||
main_updates <- m
|
main_updates <- m
|
||||||
return
|
return
|
||||||
case "main":
|
case "main":
|
||||||
playlists, err := commands.Playlists(m.ctx, m.client, (page + 1))
|
playlists, err := m.commands.Playlists(m.ctx, (page + 1))
|
||||||
page++
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -112,19 +110,18 @@ func (m *mainModel) LoadMoreItems() {
|
|||||||
main_updates <- m
|
main_updates <- m
|
||||||
return
|
return
|
||||||
case "playlist":
|
case "playlist":
|
||||||
tracks, err := commands.PlaylistTracks(m.ctx, m.client, m.playlist.ID, (page + 1))
|
playlistItems, err := m.commands.PlaylistTracks(m.ctx, m.playlist.ID, (page + 1))
|
||||||
page++
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
items := []mainItem{}
|
items := []mainItem{}
|
||||||
for _, track := range tracks.Tracks {
|
for _, item := range playlistItems.Items {
|
||||||
items = append(items, mainItem{
|
items = append(items, mainItem{
|
||||||
Name: track.Track.Name,
|
Name: item.Track.Track.Name,
|
||||||
Artist: track.Track.Artists[0],
|
Artist: item.Track.Track.Artists[0],
|
||||||
Duration: track.Track.TimeDuration().Round(time.Second).String(),
|
Duration: item.Track.Track.TimeDuration().Round(time.Second).String(),
|
||||||
ID: track.Track.ID,
|
ID: item.Track.Track.ID,
|
||||||
Desc: track.Track.Artists[0].Name + " - " + track.Track.TimeDuration().Round(time.Second).String(),
|
Desc: item.Track.Track.Artists[0].Name + " - " + item.Track.Track.TimeDuration().Round(time.Second).String(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
@ -133,12 +130,10 @@ func (m *mainModel) LoadMoreItems() {
|
|||||||
main_updates <- m
|
main_updates <- m
|
||||||
return
|
return
|
||||||
case "tracks":
|
case "tracks":
|
||||||
tracks, err := commands.TrackList(m.ctx, m.client, (page + 1))
|
tracks, err := m.commands.TrackList(m.ctx, (page + 1))
|
||||||
page++
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
page++
|
|
||||||
items := []list.Item{}
|
items := []list.Item{}
|
||||||
for _, track := range tracks.Tracks {
|
for _, track := range tracks.Tracks {
|
||||||
items = append(items, mainItem{
|
items = append(items, mainItem{
|
||||||
|
502
src/tui/main.go
502
src/tui/main.go
@ -2,10 +2,9 @@ package tui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gospt/src/gctx"
|
|
||||||
|
|
||||||
"github.com/atotto/clipboard"
|
"github.com/atotto/clipboard"
|
||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
"github.com/charmbracelet/bubbles/list"
|
"github.com/charmbracelet/bubbles/list"
|
||||||
@ -14,38 +13,45 @@ import (
|
|||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/zmb3/spotify/v2"
|
"github.com/zmb3/spotify/v2"
|
||||||
|
|
||||||
|
"git.asdf.cafe/abs3nt/gospt/src/commands"
|
||||||
|
"git.asdf.cafe/abs3nt/gospt/src/gctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
P *tea.Program
|
P *tea.Program
|
||||||
DocStyle = lipgloss.NewStyle().Margin(0, 2)
|
DocStyle = lipgloss.NewStyle().Margin(0, 2).Border(lipgloss.DoubleBorder(), true, true, true, true)
|
||||||
currentlyPlaying string
|
currentlyPlaying *spotify.CurrentlyPlaying
|
||||||
|
playbackContext string
|
||||||
main_updates chan *mainModel
|
main_updates chan *mainModel
|
||||||
page = 1
|
page = 1
|
||||||
|
loading = false
|
||||||
|
showingMessage = false
|
||||||
)
|
)
|
||||||
|
|
||||||
type Mode string
|
type Mode string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Album Mode = "album"
|
Album Mode = "album"
|
||||||
ArtistAlbum = "artistalbum"
|
ArtistAlbum Mode = "artistalbum"
|
||||||
Artist = "artist"
|
Artist Mode = "artist"
|
||||||
Artists = "artists"
|
Artists Mode = "artists"
|
||||||
Tracks = "tracks"
|
Queue Mode = "queue"
|
||||||
Albums = "albums"
|
Tracks Mode = "tracks"
|
||||||
Main = "main"
|
Albums Mode = "albums"
|
||||||
Playlists = "playlists"
|
Main Mode = "main"
|
||||||
Playlist = "playlist"
|
Playlists Mode = "playlists"
|
||||||
Devices = "devices"
|
Playlist Mode = "playlist"
|
||||||
Search = "search"
|
Devices Mode = "devices"
|
||||||
SearchAlbums = "searchalbums"
|
Search Mode = "search"
|
||||||
SearchAlbum = "searchalbum"
|
SearchAlbums Mode = "searchalbums"
|
||||||
SearchArtists = "searchartists"
|
SearchAlbum Mode = "searchalbum"
|
||||||
SearchArtist = "searchartist"
|
SearchArtists Mode = "searchartists"
|
||||||
SearchArtistAlbum = "searchartistalbum"
|
SearchArtist Mode = "searchartist"
|
||||||
SearchTracks = "searchtracks"
|
SearchArtistAlbum Mode = "searchartistalbum"
|
||||||
SearchPlaylists = "searchplaylsits"
|
SearchTracks Mode = "searchtracks"
|
||||||
SearchPlaylist = "searchplaylist"
|
SearchPlaylists Mode = "searchplaylsits"
|
||||||
|
SearchPlaylist Mode = "searchplaylist"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mainItem struct {
|
type mainItem struct {
|
||||||
@ -69,90 +75,97 @@ func (i mainItem) Description() string { return i.Desc }
|
|||||||
func (i mainItem) FilterValue() string { return i.Title() + i.Desc }
|
func (i mainItem) FilterValue() string { return i.Title() + i.Desc }
|
||||||
|
|
||||||
type mainModel struct {
|
type mainModel struct {
|
||||||
list list.Model
|
list list.Model
|
||||||
input textinput.Model
|
input textinput.Model
|
||||||
ctx *gctx.Context
|
ctx *gctx.Context
|
||||||
client *spotify.Client
|
commands *commands.Commands
|
||||||
mode Mode
|
mode Mode
|
||||||
playlist spotify.SimplePlaylist
|
playlist spotify.SimplePlaylist
|
||||||
artist spotify.SimpleArtist
|
artist spotify.SimpleArtist
|
||||||
album spotify.SimpleAlbum
|
album spotify.SimpleAlbum
|
||||||
searchResults *SearchResults
|
searchResults *SearchResults
|
||||||
progress progress.Model
|
progress progress.Model
|
||||||
playing *spotify.CurrentlyPlaying
|
playing *spotify.CurrentlyPlaying
|
||||||
search string
|
playbackContext string
|
||||||
|
search string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mainModel) PlayRadio() {
|
func (m *mainModel) PlayRadio() {
|
||||||
currentlyPlaying = m.list.SelectedItem().(mainItem).Title()
|
go m.SendMessage("Starting radio for "+m.list.SelectedItem().(mainItem).Title(), 2*time.Second)
|
||||||
m.list.NewStatusMessage("Starting radio for " + currentlyPlaying)
|
|
||||||
selectedItem := m.list.SelectedItem().(mainItem).SpotifyItem
|
selectedItem := m.list.SelectedItem().(mainItem).SpotifyItem
|
||||||
switch selectedItem.(type) {
|
switch item := selectedItem.(type) {
|
||||||
case spotify.SimplePlaylist:
|
case spotify.SimplePlaylist:
|
||||||
go HandlePlaylistRadio(m.ctx, m.client, selectedItem.(spotify.SimplePlaylist))
|
go HandlePlaylistRadio(m.ctx, m.commands, item)
|
||||||
return
|
return
|
||||||
case *spotify.SavedTrackPage:
|
case *spotify.SavedTrackPage:
|
||||||
go HandleLibraryRadio(m.ctx, m.client)
|
go HandleLibraryRadio(m.ctx, m.commands)
|
||||||
return
|
return
|
||||||
case spotify.SimpleAlbum:
|
case spotify.SimpleAlbum:
|
||||||
go HandleAlbumRadio(m.ctx, m.client, selectedItem.(spotify.SimpleAlbum).ID)
|
go HandleAlbumRadio(m.ctx, m.commands, item)
|
||||||
return
|
return
|
||||||
case spotify.FullAlbum:
|
case spotify.FullAlbum:
|
||||||
go HandleAlbumRadio(m.ctx, m.client, selectedItem.(spotify.FullAlbum).ID)
|
go HandleAlbumRadio(m.ctx, m.commands, item.SimpleAlbum)
|
||||||
return
|
return
|
||||||
case spotify.SimpleArtist:
|
case spotify.SimpleArtist:
|
||||||
go HandleArtistRadio(m.ctx, m.client, selectedItem.(spotify.SimpleArtist).ID)
|
go HandleArtistRadio(m.ctx, m.commands, item)
|
||||||
return
|
return
|
||||||
case spotify.FullArtist:
|
case spotify.FullArtist:
|
||||||
go HandleArtistRadio(m.ctx, m.client, selectedItem.(spotify.FullArtist).ID)
|
go HandleArtistRadio(m.ctx, m.commands, item.SimpleArtist)
|
||||||
return
|
return
|
||||||
default:
|
case spotify.SimpleTrack:
|
||||||
go HandleRadio(m.ctx, m.client, m.list.SelectedItem().(mainItem).ID)
|
go HandleRadio(m.ctx, m.commands, item)
|
||||||
|
return
|
||||||
|
case spotify.FullTrack:
|
||||||
|
go HandleRadio(m.ctx, m.commands, item.SimpleTrack)
|
||||||
|
return
|
||||||
|
case spotify.PlaylistTrack:
|
||||||
|
go HandleRadio(m.ctx, m.commands, item.Track.SimpleTrack)
|
||||||
|
return
|
||||||
|
case spotify.PlaylistItem:
|
||||||
|
go HandleRadio(m.ctx, m.commands, item.Track.Track.SimpleTrack)
|
||||||
|
return
|
||||||
|
case spotify.SavedTrack:
|
||||||
|
go HandleRadio(m.ctx, m.commands, item.SimpleTrack)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mainModel) GoBack() (tea.Cmd, error) {
|
func (m *mainModel) GoBack() (tea.Cmd, error) {
|
||||||
|
page = 1
|
||||||
switch m.mode {
|
switch m.mode {
|
||||||
case Main:
|
case Main:
|
||||||
return tea.Quit, nil
|
return tea.Quit, nil
|
||||||
case Albums, Artists, Tracks, Playlist, Devices, Search:
|
case Albums, Artists, Tracks, Playlist, Devices, Search, Queue:
|
||||||
m.mode = Main
|
m.mode = Main
|
||||||
m.list.NewStatusMessage("Setting view to main")
|
new_items, err := MainView(m.ctx, m.commands)
|
||||||
new_items, err := MainView(m.ctx, m.client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
return nil, err
|
||||||
}
|
}
|
||||||
m.list.SetItems(new_items)
|
m.list.SetItems(new_items)
|
||||||
case Album:
|
case Album:
|
||||||
m.mode = Albums
|
m.mode = Albums
|
||||||
m.list.NewStatusMessage("Setting view to albums")
|
new_items, err := AlbumsView(m.ctx, m.commands)
|
||||||
new_items, err := AlbumsView(m.ctx, m.client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m.list.SetItems(new_items)
|
m.list.SetItems(new_items)
|
||||||
case Artist:
|
case Artist:
|
||||||
m.mode = Artists
|
m.mode = Artists
|
||||||
m.list.NewStatusMessage("Setting view to artists")
|
new_items, err := ArtistsView(m.ctx, m.commands)
|
||||||
new_items, err := ArtistsView(m.ctx, m.client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m.list.SetItems(new_items)
|
m.list.SetItems(new_items)
|
||||||
case ArtistAlbum:
|
case ArtistAlbum:
|
||||||
m.mode = Artist
|
m.mode = Artist
|
||||||
m.list.NewStatusMessage("Opening " + m.artist.Name)
|
new_items, err := ArtistAlbumsView(m.ctx, m.artist.ID, m.commands)
|
||||||
new_items, err := ArtistAlbumsView(m.ctx, m.artist.ID, m.client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m.list.SetItems(new_items)
|
m.list.SetItems(new_items)
|
||||||
case SearchArtists, SearchTracks, SearchAlbums, SearchPlaylists:
|
case SearchArtists, SearchTracks, SearchAlbums, SearchPlaylists:
|
||||||
m.mode = Search
|
m.mode = Search
|
||||||
m.list.NewStatusMessage("Setting view to search for " + m.input.Value())
|
items, result, err := SearchView(m.ctx, m.commands, m.search)
|
||||||
items, result, err := SearchView(m.ctx, m.client, m.search)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -160,32 +173,28 @@ func (m *mainModel) GoBack() (tea.Cmd, error) {
|
|||||||
m.list.SetItems(items)
|
m.list.SetItems(items)
|
||||||
case SearchArtist:
|
case SearchArtist:
|
||||||
m.mode = SearchArtists
|
m.mode = SearchArtists
|
||||||
m.list.NewStatusMessage("Setting view to artists")
|
new_items, err := SearchArtistsView(m.ctx, m.commands, m.searchResults.Artists)
|
||||||
new_items, err := SearchArtistsView(m.ctx, m.client, m.searchResults.Artists)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m.list.SetItems(new_items)
|
m.list.SetItems(new_items)
|
||||||
case SearchArtistAlbum:
|
case SearchArtistAlbum:
|
||||||
m.mode = SearchArtist
|
m.mode = SearchArtist
|
||||||
m.list.NewStatusMessage("Opening " + m.artist.Name)
|
new_items, err := ArtistAlbumsView(m.ctx, m.artist.ID, m.commands)
|
||||||
new_items, err := ArtistAlbumsView(m.ctx, m.artist.ID, m.client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m.list.SetItems(new_items)
|
m.list.SetItems(new_items)
|
||||||
case SearchAlbum:
|
case SearchAlbum:
|
||||||
m.mode = SearchAlbums
|
m.mode = SearchAlbums
|
||||||
m.list.NewStatusMessage("Setting view to albums")
|
new_items, err := SearchAlbumsView(m.ctx, m.commands, m.searchResults.Albums)
|
||||||
new_items, err := SearchAlbumsView(m.ctx, m.client, m.searchResults.Albums)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m.list.SetItems(new_items)
|
m.list.SetItems(new_items)
|
||||||
case SearchPlaylist:
|
case SearchPlaylist:
|
||||||
m.mode = SearchPlaylists
|
m.mode = SearchPlaylists
|
||||||
m.list.NewStatusMessage("Setting view to playlists")
|
new_items, err := SearchPlaylistsView(m.ctx, m.commands, m.searchResults.Playlists)
|
||||||
new_items, err := SearchPlaylistsView(m.ctx, m.client, m.searchResults.Playlists)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -202,40 +211,122 @@ type SpotifyUrl struct {
|
|||||||
|
|
||||||
func (m *mainModel) CopyToClipboard() error {
|
func (m *mainModel) CopyToClipboard() error {
|
||||||
item := m.list.SelectedItem().(mainItem).SpotifyItem
|
item := m.list.SelectedItem().(mainItem).SpotifyItem
|
||||||
m.list.NewStatusMessage("Copying link to " + item.(mainItem).Title())
|
switch converted := item.(type) {
|
||||||
switch item.(type) {
|
|
||||||
case spotify.SimplePlaylist:
|
case spotify.SimplePlaylist:
|
||||||
clipboard.WriteAll(item.(spotify.SimplePlaylist).ExternalURLs["spotify"])
|
go m.SendMessage("Copying link to "+m.list.SelectedItem().(mainItem).Title(), 2*time.Second)
|
||||||
|
clipboard.WriteAll(converted.ExternalURLs["spotify"])
|
||||||
case *spotify.FullPlaylist:
|
case *spotify.FullPlaylist:
|
||||||
clipboard.WriteAll(item.(*spotify.FullPlaylist).ExternalURLs["spotify"])
|
go m.SendMessage("Copying link to "+m.list.SelectedItem().(mainItem).Title(), 2*time.Second)
|
||||||
|
clipboard.WriteAll(converted.ExternalURLs["spotify"])
|
||||||
case spotify.SimpleAlbum:
|
case spotify.SimpleAlbum:
|
||||||
clipboard.WriteAll(item.(spotify.SimpleAlbum).ExternalURLs["spotify"])
|
go m.SendMessage("Copying link to "+m.list.SelectedItem().(mainItem).Title(), 2*time.Second)
|
||||||
|
clipboard.WriteAll(converted.ExternalURLs["spotify"])
|
||||||
case *spotify.FullAlbum:
|
case *spotify.FullAlbum:
|
||||||
clipboard.WriteAll(item.(*spotify.FullAlbum).ExternalURLs["spotify"])
|
go m.SendMessage("Copying link to "+m.list.SelectedItem().(mainItem).Title(), 2*time.Second)
|
||||||
|
clipboard.WriteAll(converted.ExternalURLs["spotify"])
|
||||||
case spotify.SimpleArtist:
|
case spotify.SimpleArtist:
|
||||||
clipboard.WriteAll(item.(spotify.SimpleArtist).ExternalURLs["spotify"])
|
go m.SendMessage("Copying link to "+m.list.SelectedItem().(mainItem).Title(), 2*time.Second)
|
||||||
|
clipboard.WriteAll(converted.ExternalURLs["spotify"])
|
||||||
case *spotify.FullArtist:
|
case *spotify.FullArtist:
|
||||||
clipboard.WriteAll(item.(*spotify.FullArtist).ExternalURLs["spotify"])
|
go m.SendMessage("Copying link to "+m.list.SelectedItem().(mainItem).Title(), 2*time.Second)
|
||||||
|
clipboard.WriteAll(converted.ExternalURLs["spotify"])
|
||||||
case spotify.SimpleTrack:
|
case spotify.SimpleTrack:
|
||||||
clipboard.WriteAll(item.(spotify.SimpleTrack).ExternalURLs["spotify"])
|
go m.SendMessage("Copying link to "+m.list.SelectedItem().(mainItem).Title(), 2*time.Second)
|
||||||
|
clipboard.WriteAll(converted.ExternalURLs["spotify"])
|
||||||
case spotify.PlaylistTrack:
|
case spotify.PlaylistTrack:
|
||||||
clipboard.WriteAll(item.(spotify.PlaylistTrack).Track.ExternalURLs["spotify"])
|
go m.SendMessage("Copying link to "+m.list.SelectedItem().(mainItem).Title(), 2*time.Second)
|
||||||
|
clipboard.WriteAll(converted.Track.ExternalURLs["spotify"])
|
||||||
case spotify.SavedTrack:
|
case spotify.SavedTrack:
|
||||||
clipboard.WriteAll(item.(spotify.SavedTrack).ExternalURLs["spotify"])
|
go m.SendMessage("Copying link to "+m.list.SelectedItem().(mainItem).Title(), 2*time.Second)
|
||||||
|
clipboard.WriteAll(converted.ExternalURLs["spotify"])
|
||||||
case spotify.FullTrack:
|
case spotify.FullTrack:
|
||||||
clipboard.WriteAll(item.(spotify.FullTrack).ExternalURLs["spotify"])
|
go m.SendMessage("Copying link to "+m.list.SelectedItem().(mainItem).Title(), 2*time.Second)
|
||||||
|
clipboard.WriteAll(converted.ExternalURLs["spotify"])
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mainModel) SendMessage(msg string, duration time.Duration) {
|
||||||
|
showingMessage = true
|
||||||
|
defer func() {
|
||||||
|
showingMessage = false
|
||||||
|
}()
|
||||||
|
m.list.NewStatusMessage(msg)
|
||||||
|
time.Sleep(duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mainModel) QueueItem() error {
|
||||||
|
switch item := m.list.SelectedItem().(mainItem).SpotifyItem.(type) {
|
||||||
|
case spotify.PlaylistTrack:
|
||||||
|
go m.SendMessage("Adding "+item.Track.Name+" to queue", 2*time.Second)
|
||||||
|
go HandleQueueItem(m.ctx, m.commands, item.Track.ID)
|
||||||
|
case spotify.SavedTrack:
|
||||||
|
go m.SendMessage("Adding "+item.Name+" to queue", 2*time.Second)
|
||||||
|
go HandleQueueItem(m.ctx, m.commands, item.ID)
|
||||||
|
case spotify.SimpleTrack:
|
||||||
|
go m.SendMessage("Adding "+item.Name+" to queue", 2*time.Second)
|
||||||
|
go HandleQueueItem(m.ctx, m.commands, item.ID)
|
||||||
|
case spotify.FullTrack:
|
||||||
|
go m.SendMessage("Adding "+item.Name+" to queue", 2*time.Second)
|
||||||
|
go HandleQueueItem(m.ctx, m.commands, item.ID)
|
||||||
|
case *spotify.FullTrack:
|
||||||
|
go m.SendMessage("Adding "+item.Name+" to queue", 2*time.Second)
|
||||||
|
go HandleQueueItem(m.ctx, m.commands, item.ID)
|
||||||
|
case *spotify.SimpleTrack:
|
||||||
|
go m.SendMessage("Adding "+item.Name+" to queue", 2*time.Second)
|
||||||
|
go HandleQueueItem(m.ctx, m.commands, item.ID)
|
||||||
|
case *spotify.SimplePlaylist:
|
||||||
|
go m.SendMessage("Adding "+item.Name+" to queue", 2*time.Second)
|
||||||
|
go HandleQueueItem(m.ctx, m.commands, item.ID)
|
||||||
|
}
|
||||||
|
if m.mode == Queue {
|
||||||
|
go func() {
|
||||||
|
new_items, err := QueueView(m.ctx, m.commands)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.list.SetItems(new_items)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mainModel) DeleteTrackFromPlaylist() error {
|
||||||
|
if m.mode != Playlist {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
track := m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.PlaylistTrack).Track
|
||||||
|
go m.SendMessage("Deleteing "+track.Name+" from "+m.playlist.Name, 2*time.Second)
|
||||||
|
go func() {
|
||||||
|
HandleDeleteTrackFromPlaylist(m.ctx, m.commands, track.ID, m.playlist.ID)
|
||||||
|
new_items, err := PlaylistView(m.ctx, m.commands, m.playlist)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.list.SetItems(new_items)
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *mainModel) SelectItem() error {
|
func (m *mainModel) SelectItem() error {
|
||||||
switch m.mode {
|
switch m.mode {
|
||||||
|
case Queue:
|
||||||
|
page = 1
|
||||||
|
go func() {
|
||||||
|
HandleNextInQueue(m.ctx, m.commands, m.list.Index())
|
||||||
|
new_items, err := QueueView(m.ctx, m.commands)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.list.SetItems(new_items)
|
||||||
|
m.list.ResetSelected()
|
||||||
|
}()
|
||||||
case Search:
|
case Search:
|
||||||
switch m.list.SelectedItem().(mainItem).SpotifyItem.(type) {
|
page = 1
|
||||||
|
switch item := m.list.SelectedItem().(mainItem).SpotifyItem.(type) {
|
||||||
case *spotify.FullArtistPage:
|
case *spotify.FullArtistPage:
|
||||||
m.mode = SearchArtists
|
m.mode = SearchArtists
|
||||||
m.list.NewStatusMessage("Setting view to artists")
|
new_items, err := SearchArtistsView(m.ctx, m.commands, item)
|
||||||
new_items, err := SearchArtistsView(m.ctx, m.client, m.list.SelectedItem().(mainItem).SpotifyItem.(*spotify.FullArtistPage))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -243,8 +334,7 @@ func (m *mainModel) SelectItem() error {
|
|||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
case *spotify.SimpleAlbumPage:
|
case *spotify.SimpleAlbumPage:
|
||||||
m.mode = SearchAlbums
|
m.mode = SearchAlbums
|
||||||
m.list.NewStatusMessage("Setting view to albums")
|
new_items, err := SearchAlbumsView(m.ctx, m.commands, item)
|
||||||
new_items, err := SearchAlbumsView(m.ctx, m.client, m.list.SelectedItem().(mainItem).SpotifyItem.(*spotify.SimpleAlbumPage))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -252,9 +342,7 @@ func (m *mainModel) SelectItem() error {
|
|||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
case *spotify.SimplePlaylistPage:
|
case *spotify.SimplePlaylistPage:
|
||||||
m.mode = SearchPlaylists
|
m.mode = SearchPlaylists
|
||||||
playlists := m.list.SelectedItem().(mainItem).SpotifyItem.(*spotify.SimplePlaylistPage)
|
new_items, err := SearchPlaylistsView(m.ctx, m.commands, item)
|
||||||
m.list.NewStatusMessage("Setting view to playlist")
|
|
||||||
new_items, err := SearchPlaylistsView(m.ctx, m.client, playlists)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -262,62 +350,68 @@ func (m *mainModel) SelectItem() error {
|
|||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
case *spotify.FullTrackPage:
|
case *spotify.FullTrackPage:
|
||||||
m.mode = SearchTracks
|
m.mode = SearchTracks
|
||||||
m.list.NewStatusMessage("Setting view to tracks")
|
new_items, err := SearchTracksView(m.ctx, m.commands, item)
|
||||||
new_items, err := SearchTracksView(m.ctx, m.client, m.list.SelectedItem().(mainItem).SpotifyItem.(*spotify.FullTrackPage))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m.list.SetItems(new_items)
|
m.list.SetItems(new_items)
|
||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
m.list.NewStatusMessage("Setting view to tracks")
|
|
||||||
}
|
}
|
||||||
case SearchArtists:
|
case SearchArtists:
|
||||||
|
page = 1
|
||||||
m.mode = SearchArtist
|
m.mode = SearchArtist
|
||||||
m.artist = m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.SimpleArtist)
|
m.artist = m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.SimpleArtist)
|
||||||
m.list.NewStatusMessage("Opening " + m.artist.Name)
|
new_items, err := ArtistAlbumsView(m.ctx, m.artist.ID, m.commands)
|
||||||
new_items, err := ArtistAlbumsView(m.ctx, m.artist.ID, m.client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m.list.SetItems(new_items)
|
m.list.SetItems(new_items)
|
||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
case SearchArtist:
|
case SearchArtist:
|
||||||
|
page = 1
|
||||||
m.mode = SearchArtistAlbum
|
m.mode = SearchArtistAlbum
|
||||||
m.album = m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.SimpleAlbum)
|
m.album = m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.SimpleAlbum)
|
||||||
m.list.NewStatusMessage("Opening " + m.album.Name)
|
new_items, err := AlbumTracksView(m.ctx, m.album.ID, m.commands)
|
||||||
new_items, err := AlbumTracksView(m.ctx, m.album.ID, m.client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m.list.SetItems(new_items)
|
m.list.SetItems(new_items)
|
||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
case SearchAlbums:
|
case SearchAlbums:
|
||||||
|
page = 1
|
||||||
m.mode = SearchAlbum
|
m.mode = SearchAlbum
|
||||||
m.album = m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.SimpleAlbum)
|
m.album = m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.SimpleAlbum)
|
||||||
m.list.NewStatusMessage("Opening " + m.album.Name)
|
new_items, err := AlbumTracksView(m.ctx, m.album.ID, m.commands)
|
||||||
new_items, err := AlbumTracksView(m.ctx, m.album.ID, m.client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m.list.SetItems(new_items)
|
m.list.SetItems(new_items)
|
||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
case SearchPlaylists:
|
case SearchPlaylists:
|
||||||
|
page = 1
|
||||||
m.mode = SearchPlaylist
|
m.mode = SearchPlaylist
|
||||||
playlist := m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.SimplePlaylist)
|
playlist := m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.SimplePlaylist)
|
||||||
m.playlist = playlist
|
m.playlist = playlist
|
||||||
m.list.NewStatusMessage("Setting view to playlist " + playlist.Name)
|
new_items, err := PlaylistView(m.ctx, m.commands, playlist)
|
||||||
new_items, err := PlaylistView(m.ctx, m.client, playlist)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m.list.SetItems(new_items)
|
m.list.SetItems(new_items)
|
||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
case Main:
|
case Main:
|
||||||
switch m.list.SelectedItem().(mainItem).SpotifyItem.(type) {
|
page = 1
|
||||||
|
switch item := m.list.SelectedItem().(mainItem).SpotifyItem.(type) {
|
||||||
|
case spotify.Queue:
|
||||||
|
m.mode = Queue
|
||||||
|
new_items, err := QueueView(m.ctx, m.commands)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.list.SetItems(new_items)
|
||||||
|
m.list.ResetSelected()
|
||||||
case *spotify.FullArtistCursorPage:
|
case *spotify.FullArtistCursorPage:
|
||||||
m.mode = Artists
|
m.mode = Artists
|
||||||
m.list.NewStatusMessage("Setting view to artists")
|
new_items, err := ArtistsView(m.ctx, m.commands)
|
||||||
new_items, err := ArtistsView(m.ctx, m.client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -325,8 +419,7 @@ func (m *mainModel) SelectItem() error {
|
|||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
case *spotify.SavedAlbumPage:
|
case *spotify.SavedAlbumPage:
|
||||||
m.mode = Albums
|
m.mode = Albums
|
||||||
m.list.NewStatusMessage("Setting view to albums")
|
new_items, err := AlbumsView(m.ctx, m.commands)
|
||||||
new_items, err := AlbumsView(m.ctx, m.client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -334,10 +427,8 @@ func (m *mainModel) SelectItem() error {
|
|||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
case spotify.SimplePlaylist:
|
case spotify.SimplePlaylist:
|
||||||
m.mode = Playlist
|
m.mode = Playlist
|
||||||
playlist := m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.SimplePlaylist)
|
m.playlist = item
|
||||||
m.playlist = playlist
|
new_items, err := PlaylistView(m.ctx, m.commands, item)
|
||||||
m.list.NewStatusMessage("Setting view to playlist " + playlist.Name)
|
|
||||||
new_items, err := PlaylistView(m.ctx, m.client, playlist)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -345,20 +436,18 @@ func (m *mainModel) SelectItem() error {
|
|||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
case *spotify.SavedTrackPage:
|
case *spotify.SavedTrackPage:
|
||||||
m.mode = Tracks
|
m.mode = Tracks
|
||||||
m.list.NewStatusMessage("Setting view to saved tracks")
|
new_items, err := SavedTracksView(m.ctx, m.commands)
|
||||||
new_items, err := SavedTracksView(m.ctx, m.client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m.list.SetItems(new_items)
|
m.list.SetItems(new_items)
|
||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
m.list.NewStatusMessage("Setting view to tracks")
|
|
||||||
}
|
}
|
||||||
case Albums:
|
case Albums:
|
||||||
|
page = 1
|
||||||
m.mode = Album
|
m.mode = Album
|
||||||
m.album = m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.SimpleAlbum)
|
m.album = m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.SimpleAlbum)
|
||||||
m.list.NewStatusMessage("Opening " + m.album.Name)
|
new_items, err := AlbumTracksView(m.ctx, m.album.ID, m.commands)
|
||||||
new_items, err := AlbumTracksView(m.ctx, m.album.ID, m.client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -367,8 +456,7 @@ func (m *mainModel) SelectItem() error {
|
|||||||
case Artist:
|
case Artist:
|
||||||
m.mode = ArtistAlbum
|
m.mode = ArtistAlbum
|
||||||
m.album = m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.SimpleAlbum)
|
m.album = m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.SimpleAlbum)
|
||||||
m.list.NewStatusMessage("Opening " + m.album.Name)
|
new_items, err := AlbumTracksView(m.ctx, m.album.ID, m.commands)
|
||||||
new_items, err := AlbumTracksView(m.ctx, m.album.ID, m.client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -377,44 +465,36 @@ func (m *mainModel) SelectItem() error {
|
|||||||
case Artists:
|
case Artists:
|
||||||
m.mode = Artist
|
m.mode = Artist
|
||||||
m.artist = m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.SimpleArtist)
|
m.artist = m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.SimpleArtist)
|
||||||
m.list.NewStatusMessage("Opening " + m.artist.Name)
|
new_items, err := ArtistAlbumsView(m.ctx, m.artist.ID, m.commands)
|
||||||
new_items, err := ArtistAlbumsView(m.ctx, m.artist.ID, m.client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m.list.SetItems(new_items)
|
m.list.SetItems(new_items)
|
||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
case Album, ArtistAlbum, SearchArtistAlbum, SearchAlbum:
|
case Album, ArtistAlbum, SearchArtistAlbum, SearchAlbum:
|
||||||
currentlyPlaying = m.list.SelectedItem().FilterValue()
|
pos := m.list.Cursor() + (m.list.Paginator.Page * m.list.Paginator.TotalPages)
|
||||||
m.list.NewStatusMessage("Playing " + currentlyPlaying)
|
go HandlePlayWithContext(m.ctx, m.commands, &m.album.URI, &pos)
|
||||||
go HandlePlayWithContext(m.ctx, m.client, &m.album.URI, m.list.Cursor()+(m.list.Paginator.Page*m.list.Paginator.TotalPages))
|
|
||||||
case Playlist, SearchPlaylist:
|
case Playlist, SearchPlaylist:
|
||||||
currentlyPlaying = m.list.SelectedItem().FilterValue()
|
pos := m.list.Cursor() + (m.list.Paginator.Page * m.list.Paginator.PerPage)
|
||||||
m.list.NewStatusMessage("Playing " + currentlyPlaying)
|
go HandlePlayWithContext(m.ctx, m.commands, &m.playlist.URI, &pos)
|
||||||
go HandlePlayWithContext(m.ctx, m.client, &m.playlist.URI, m.list.Cursor()+(m.list.Paginator.Page*m.list.Paginator.PerPage))
|
|
||||||
case Tracks:
|
case Tracks:
|
||||||
currentlyPlaying = m.list.SelectedItem().FilterValue()
|
go HandlePlayLikedSong(m.ctx, m.commands, m.list.Cursor()+(m.list.Paginator.Page*m.list.Paginator.PerPage))
|
||||||
m.list.NewStatusMessage("Playing " + currentlyPlaying)
|
|
||||||
go HandlePlayLikedSong(m.ctx, m.client, m.list.Cursor()+(m.list.Paginator.Page*m.list.Paginator.PerPage))
|
|
||||||
case SearchTracks:
|
case SearchTracks:
|
||||||
currentlyPlaying = m.list.SelectedItem().FilterValue()
|
go HandlePlayTrack(m.ctx, m.commands, m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.FullTrack).ID)
|
||||||
m.list.NewStatusMessage("Playing " + currentlyPlaying)
|
|
||||||
go HandlePlayTrack(m.ctx, m.client, m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.FullTrack).ID)
|
|
||||||
case Devices:
|
case Devices:
|
||||||
go HandleSetDevice(m.ctx, m.client, m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.PlayerDevice))
|
go HandleSetDevice(m.ctx, m.commands, m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.PlayerDevice))
|
||||||
m.list.NewStatusMessage("Setting device to " + m.list.SelectedItem().FilterValue())
|
go m.SendMessage("Setting device to "+m.list.SelectedItem().FilterValue(), 2*time.Second)
|
||||||
m.mode = "main"
|
m.mode = "main"
|
||||||
m.list.NewStatusMessage("Setting view to main")
|
new_items, err := MainView(m.ctx, m.commands)
|
||||||
new_items, err := MainView(m.ctx, m.client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
return err
|
||||||
}
|
}
|
||||||
m.list.SetItems(new_items)
|
m.list.SetItems(new_items)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m mainModel) Init() tea.Cmd {
|
func (m *mainModel) Init() tea.Cmd {
|
||||||
main_updates = make(chan *mainModel)
|
main_updates = make(chan *mainModel)
|
||||||
return Tick()
|
return Tick()
|
||||||
}
|
}
|
||||||
@ -427,7 +507,32 @@ func Tick() tea.Cmd {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m mainModel) View() string {
|
func (m *mainModel) TickPlayback() {
|
||||||
|
playing, _ := m.commands.Client().PlayerCurrentlyPlaying(m.ctx)
|
||||||
|
if playing != nil && playing.Playing && playing.Item != nil {
|
||||||
|
currentlyPlaying = playing
|
||||||
|
playbackContext, _ = m.getContext(playing)
|
||||||
|
}
|
||||||
|
ticker := time.NewTicker(1 * time.Second)
|
||||||
|
quit := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
playing, _ := m.commands.Client().PlayerCurrentlyPlaying(m.ctx)
|
||||||
|
if playing != nil && playing.Playing && playing.Item != nil {
|
||||||
|
currentlyPlaying = playing
|
||||||
|
playbackContext, _ = m.getContext(playing)
|
||||||
|
}
|
||||||
|
case <-quit:
|
||||||
|
ticker.Stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mainModel) View() string {
|
||||||
if m.input.Focused() {
|
if m.input.Focused() {
|
||||||
return DocStyle.Render(m.list.View() + "\n" + m.input.View())
|
return DocStyle.Render(m.list.View() + "\n" + m.input.View())
|
||||||
}
|
}
|
||||||
@ -436,10 +541,8 @@ func (m mainModel) View() string {
|
|||||||
|
|
||||||
func (m *mainModel) Typing(msg tea.KeyMsg) (bool, tea.Cmd) {
|
func (m *mainModel) Typing(msg tea.KeyMsg) (bool, tea.Cmd) {
|
||||||
if msg.String() == "enter" {
|
if msg.String() == "enter" {
|
||||||
m.list.NewStatusMessage("Setting view to search for " + m.input.Value())
|
items, result, err := SearchView(m.ctx, m.commands, m.input.Value())
|
||||||
items, result, err := SearchView(m.ctx, m.client, m.input.Value())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
|
||||||
return false, tea.Quit
|
return false, tea.Quit
|
||||||
}
|
}
|
||||||
m.searchResults = result
|
m.searchResults = result
|
||||||
@ -459,9 +562,37 @@ func (m *mainModel) Typing(msg tea.KeyMsg) (bool, tea.Cmd) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m *mainModel) getContext(playing *spotify.CurrentlyPlaying) (string, error) {
|
||||||
// Update Now Playing
|
context := playing.PlaybackContext
|
||||||
m.list.NewStatusMessage(currentlyPlaying)
|
uri_split := strings.Split(string(context.URI), ":")
|
||||||
|
if len(uri_split) < 3 {
|
||||||
|
return "", fmt.Errorf("NO URI")
|
||||||
|
}
|
||||||
|
id := strings.Split(string(context.URI), ":")[2]
|
||||||
|
switch context.Type {
|
||||||
|
case "album":
|
||||||
|
album, err := m.commands.Client().GetAlbum(m.ctx, spotify.ID(id))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return album.Name, nil
|
||||||
|
case "playlist":
|
||||||
|
playlist, err := m.commands.Client().GetPlaylist(m.ctx, spotify.ID(id))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return playlist.Name, nil
|
||||||
|
case "artist":
|
||||||
|
artist, err := m.commands.Client().GetArtist(m.ctx, spotify.ID(id))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return artist.Name, nil
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
// Update list items from LoadMore
|
// Update list items from LoadMore
|
||||||
select {
|
select {
|
||||||
case update := <-main_updates:
|
case update := <-main_updates:
|
||||||
@ -469,7 +600,7 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
// Call for more items if needed
|
// Call for more items if needed
|
||||||
if m.list.Paginator.Page == m.list.Paginator.TotalPages-2 && m.list.Cursor() == 0 {
|
if m.list.Paginator.Page == m.list.Paginator.TotalPages-1 && m.list.Cursor() == 0 && !loading {
|
||||||
// if last request was still full request more
|
// if last request was still full request more
|
||||||
if len(m.list.Items())%50 == 0 {
|
if len(m.list.Items())%50 == 0 {
|
||||||
go m.LoadMoreItems()
|
go m.LoadMoreItems()
|
||||||
@ -478,20 +609,40 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
// Handle user input
|
// Handle user input
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case tickMsg:
|
case tickMsg:
|
||||||
playing, err := m.client.PlayerCurrentlyPlaying(m.ctx)
|
playing := currentlyPlaying
|
||||||
if err != nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
if playing != nil && playing.Playing && playing.Item != nil {
|
if playing != nil && playing.Playing && playing.Item != nil {
|
||||||
cmd := m.progress.SetPercent(float64(playing.Progress) / float64(playing.Item.Duration))
|
cmd := m.progress.SetPercent(float64(playing.Progress) / float64(playing.Item.Duration))
|
||||||
m.playing = playing
|
m.playing = playing
|
||||||
|
m.playbackContext = playbackContext
|
||||||
|
if m.mode == Queue && len(m.list.Items()) != 0 {
|
||||||
|
if m.list.Items()[0].(mainItem).SpotifyItem.(spotify.FullTrack).Name != playing.Item.Name {
|
||||||
|
go func() {
|
||||||
|
new_items, err := QueueView(m.ctx, m.commands)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.list.SetItems(new_items)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
return m, tea.Batch(Tick(), cmd)
|
return m, tea.Batch(Tick(), cmd)
|
||||||
}
|
}
|
||||||
return m, Tick()
|
return m, Tick()
|
||||||
|
|
||||||
case progress.FrameMsg:
|
case progress.FrameMsg:
|
||||||
progressModel, cmd := m.progress.Update(msg)
|
progressModel, cmd := m.progress.Update(msg)
|
||||||
m.progress = progressModel.(progress.Model)
|
m.progress = progressModel.(progress.Model)
|
||||||
currentlyPlaying = fmt.Sprintf("Now playing %s by %s - %s %s/%s ", m.playing.Item.Name, m.playing.Item.Artists[0].Name, m.progress.View(), (time.Duration(m.playing.Progress) * time.Millisecond).Round(time.Second), (time.Duration(m.playing.Item.Duration) * time.Millisecond).Round(time.Second))
|
if !showingMessage {
|
||||||
|
m.list.NewStatusMessage(
|
||||||
|
fmt.Sprintf("Now playing %s by %s - %s %s/%s : %s",
|
||||||
|
m.playing.Item.Name,
|
||||||
|
m.playing.Item.Artists[0].Name,
|
||||||
|
m.progress.View(),
|
||||||
|
(time.Duration(m.playing.Progress) * time.Millisecond).Round(time.Second),
|
||||||
|
(time.Duration(m.playing.Item.Duration) * time.Millisecond).Round(time.Second),
|
||||||
|
m.playbackContext),
|
||||||
|
)
|
||||||
|
}
|
||||||
return m, cmd
|
return m, cmd
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
// quit
|
// quit
|
||||||
@ -505,16 +656,16 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if msg.String() == ">" {
|
if msg.String() == ">" {
|
||||||
go HandleSeek(m.ctx, m.client, true)
|
go HandleSeek(m.ctx, m.commands, true)
|
||||||
}
|
}
|
||||||
if msg.String() == "<" {
|
if msg.String() == "<" {
|
||||||
go HandleSeek(m.ctx, m.client, false)
|
go HandleSeek(m.ctx, m.commands, false)
|
||||||
}
|
}
|
||||||
if msg.String() == "+" {
|
if msg.String() == "+" {
|
||||||
go HandleVolume(m.ctx, m.client, true)
|
go HandleVolume(m.ctx, m.commands, true)
|
||||||
}
|
}
|
||||||
if msg.String() == "-" {
|
if msg.String() == "-" {
|
||||||
go HandleVolume(m.ctx, m.client, false)
|
go HandleVolume(m.ctx, m.commands, false)
|
||||||
}
|
}
|
||||||
// search input
|
// search input
|
||||||
if m.input.Focused() {
|
if m.input.Focused() {
|
||||||
@ -531,27 +682,36 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
// enter device selection
|
// enter device selection
|
||||||
if msg.String() == "d" {
|
if msg.String() == "d" {
|
||||||
m.mode = Devices
|
m.mode = Devices
|
||||||
new_items, err := DeviceView(m.ctx, m.client)
|
new_items, err := DeviceView(m.ctx, m.commands)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
}
|
}
|
||||||
m.list.SetItems(new_items)
|
m.list.SetItems(new_items)
|
||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
m.list.NewStatusMessage("Setting view to devices")
|
|
||||||
}
|
}
|
||||||
// go back
|
// go back
|
||||||
if msg.String() == "backspace" || msg.String() == "esc" || msg.String() == "q" {
|
if msg.String() == "backspace" || msg.String() == "esc" || msg.String() == "q" {
|
||||||
msg, err := m.GoBack()
|
msg, err := m.GoBack()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
return m, tea.Quit
|
||||||
}
|
}
|
||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
return m, msg
|
return m, msg
|
||||||
}
|
}
|
||||||
|
if msg.String() == "ctrl+d" {
|
||||||
|
err := m.DeleteTrackFromPlaylist()
|
||||||
|
if err != nil {
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msg.String() == "ctrl+@" || msg.String() == "ctrl+p" {
|
||||||
|
err := m.QueueItem()
|
||||||
|
if err != nil {
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
}
|
||||||
// select item
|
// select item
|
||||||
if msg.String() == "enter" || msg.String() == "spacebar" {
|
if msg.String() == "enter" || msg.String() == " " || msg.String() == "p" {
|
||||||
err := m.SelectItem()
|
err := m.SelectItem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
@ -583,59 +743,63 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
return m, cmd
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitMain(ctx *gctx.Context, client *spotify.Client, mode Mode) (tea.Model, error) {
|
func InitMain(ctx *gctx.Context, c *commands.Commands, mode Mode) (tea.Model, error) {
|
||||||
prog := progress.New(progress.WithColorProfile(2), progress.WithoutPercentage())
|
prog := progress.New(progress.WithColorProfile(2), progress.WithoutPercentage())
|
||||||
var err error
|
var err error
|
||||||
lipgloss.SetColorProfile(2)
|
lipgloss.SetColorProfile(2)
|
||||||
items := []list.Item{}
|
items := []list.Item{}
|
||||||
switch mode {
|
switch mode {
|
||||||
case Main:
|
case Main:
|
||||||
items, err = MainView(ctx, client)
|
items, err = MainView(ctx, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case Devices:
|
case Devices:
|
||||||
items, err = DeviceView(ctx, client)
|
items, err = DeviceView(ctx, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case Tracks:
|
case Tracks:
|
||||||
items, err = SavedTracksView(ctx, client)
|
items, err = SavedTracksView(ctx, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m := mainModel{
|
m := &mainModel{
|
||||||
list: list.New(items, list.NewDefaultDelegate(), 0, 0),
|
list: list.New(items, list.NewDefaultDelegate(), 0, 0),
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
client: client,
|
commands: c,
|
||||||
mode: mode,
|
mode: mode,
|
||||||
progress: prog,
|
progress: prog,
|
||||||
}
|
}
|
||||||
m.list.Title = "GOSPT"
|
m.list.Title = "GOSPT"
|
||||||
|
go m.TickPlayback()
|
||||||
Tick()
|
Tick()
|
||||||
m.list.DisableQuitKeybindings()
|
m.list.DisableQuitKeybindings()
|
||||||
|
m.list.SetFilteringEnabled(false)
|
||||||
m.list.AdditionalShortHelpKeys = func() []key.Binding {
|
m.list.AdditionalShortHelpKeys = func() []key.Binding {
|
||||||
return []key.Binding{
|
return []key.Binding{
|
||||||
key.NewBinding(key.WithKeys("esc"), key.WithHelp("esc", "back")),
|
key.NewBinding(key.WithKeys("q"), key.WithHelp("q", "back")),
|
||||||
key.NewBinding(key.WithKeys("s"), key.WithHelp("s", "search")),
|
key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "select")),
|
||||||
key.NewBinding(key.WithKeys("ctrl+c"), key.WithHelp("ctrl+c", "quit")),
|
key.NewBinding(key.WithKeys("/"), key.WithHelp("/", "search")),
|
||||||
key.NewBinding(key.WithKeys("ctrl"+"r"), key.WithHelp("ctrl+r", "start radio")),
|
key.NewBinding(key.WithKeys("ctrl"+"r"), key.WithHelp("ctrl+r", "radio")),
|
||||||
key.NewBinding(key.WithKeys("ctrl"+"shift"+"c"), key.WithHelp("ctrl+shift+c", "copy url")),
|
key.NewBinding(key.WithKeys("ctrl"+"p"), key.WithHelp("ctrl+p", "queue")),
|
||||||
key.NewBinding(key.WithKeys("d"), key.WithHelp("d", "select device")),
|
key.NewBinding(key.WithKeys("d"), key.WithHelp("d", "select device")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m.list.AdditionalFullHelpKeys = func() []key.Binding {
|
m.list.AdditionalFullHelpKeys = func() []key.Binding {
|
||||||
return []key.Binding{
|
return []key.Binding{
|
||||||
key.NewBinding(key.WithKeys("esc"), key.WithHelp("esc", "back")),
|
key.NewBinding(key.WithKeys("esc"), key.WithHelp("esc", "back")),
|
||||||
key.NewBinding(key.WithKeys("s"), key.WithHelp("s", "search")),
|
key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "select")),
|
||||||
|
key.NewBinding(key.WithKeys("/"), key.WithHelp("/", "search")),
|
||||||
key.NewBinding(key.WithKeys(">"), key.WithHelp(">", "seek forward")),
|
key.NewBinding(key.WithKeys(">"), key.WithHelp(">", "seek forward")),
|
||||||
key.NewBinding(key.WithKeys("<"), key.WithHelp("<", "seek backward")),
|
key.NewBinding(key.WithKeys("<"), key.WithHelp("<", "seek backward")),
|
||||||
key.NewBinding(key.WithKeys("+"), key.WithHelp("+", "volume up")),
|
key.NewBinding(key.WithKeys("+"), key.WithHelp("+", "volume up")),
|
||||||
key.NewBinding(key.WithKeys("-"), key.WithHelp("-", "volume down")),
|
key.NewBinding(key.WithKeys("-"), key.WithHelp("-", "volume down")),
|
||||||
|
key.NewBinding(key.WithKeys("c"), key.WithHelp("c", "copy link to item")),
|
||||||
key.NewBinding(key.WithKeys("ctrl+c"), key.WithHelp("ctrl+c", "quit")),
|
key.NewBinding(key.WithKeys("ctrl+c"), key.WithHelp("ctrl+c", "quit")),
|
||||||
key.NewBinding(key.WithKeys("ctrl"+"r"), key.WithHelp("ctrl+r", "start radio")),
|
key.NewBinding(key.WithKeys("ctrl"+"r"), key.WithHelp("ctrl+r", "start radio")),
|
||||||
key.NewBinding(key.WithKeys("ctrl"+"shift"+"c"), key.WithHelp("ctrl+shift+c", "copy url")),
|
key.NewBinding(key.WithKeys("ctrl"+"p"), key.WithHelp("ctrl+p", "queue song")),
|
||||||
key.NewBinding(key.WithKeys("d"), key.WithHelp("d", "select device")),
|
key.NewBinding(key.WithKeys("d"), key.WithHelp("d", "select device")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,20 @@
|
|||||||
package tui
|
package tui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"git.asdf.cafe/abs3nt/gospt/src/commands"
|
||||||
|
"git.asdf.cafe/abs3nt/gospt/src/gctx"
|
||||||
"gospt/src/gctx"
|
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/zmb3/spotify/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// StartTea the entry point for the UI. Initializes the model.
|
// StartTea the entry point for the UI. Initializes the model.
|
||||||
func StartTea(ctx *gctx.Context, client *spotify.Client, mode string) error {
|
func StartTea(ctx *gctx.Context, cmd *commands.Commands, mode string) error {
|
||||||
m, err := InitMain(ctx, client, Mode(mode))
|
m, err := InitMain(ctx, cmd, Mode(mode))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("UH OH")
|
return err
|
||||||
}
|
}
|
||||||
P = tea.NewProgram(m, tea.WithAltScreen())
|
P = tea.NewProgram(m, tea.WithAltScreen())
|
||||||
if err := P.Start(); err != nil {
|
if _, err := P.Run(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
205
src/tui/views.go
205
src/tui/views.go
@ -2,19 +2,22 @@ package tui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gospt/src/commands"
|
"git.asdf.cafe/abs3nt/gospt/src/commands"
|
||||||
"gospt/src/gctx"
|
"git.asdf.cafe/abs3nt/gospt/src/gctx"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
"github.com/charmbracelet/bubbles/list"
|
"github.com/charmbracelet/bubbles/list"
|
||||||
"github.com/zmb3/spotify/v2"
|
"github.com/zmb3/spotify/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func DeviceView(ctx *gctx.Context, client *spotify.Client) ([]list.Item, error) {
|
const regex = `<.*?>`
|
||||||
|
|
||||||
|
func DeviceView(ctx *gctx.Context, commands *commands.Commands) ([]list.Item, error) {
|
||||||
items := []list.Item{}
|
items := []list.Item{}
|
||||||
devices, err := client.PlayerDevices(ctx)
|
devices, err := commands.Client().PlayerDevices(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -28,28 +31,57 @@ func DeviceView(ctx *gctx.Context, client *spotify.Client) ([]list.Item, error)
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func PlaylistView(ctx *gctx.Context, client *spotify.Client, playlist spotify.SimplePlaylist) ([]list.Item, error) {
|
func QueueView(ctx *gctx.Context, commands *commands.Commands) ([]list.Item, error) {
|
||||||
items := []list.Item{}
|
items := []list.Item{}
|
||||||
tracks, err := commands.PlaylistTracks(ctx, client, playlist.ID, 1)
|
tracks, err := commands.UserQueue(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, track := range tracks.Tracks {
|
if tracks.CurrentlyPlaying.Name != "" {
|
||||||
items = append(items, mainItem{
|
items = append(items, mainItem{
|
||||||
Name: track.Track.Name,
|
Name: tracks.CurrentlyPlaying.Name,
|
||||||
Artist: track.Track.Artists[0],
|
Artist: tracks.CurrentlyPlaying.Artists[0],
|
||||||
Duration: track.Track.TimeDuration().Round(time.Second).String(),
|
Duration: tracks.CurrentlyPlaying.TimeDuration().Round(time.Second).String(),
|
||||||
ID: track.Track.ID,
|
ID: tracks.CurrentlyPlaying.ID,
|
||||||
Desc: track.Track.Artists[0].Name + " - " + track.Track.TimeDuration().Round(time.Second).String(),
|
Desc: tracks.CurrentlyPlaying.Artists[0].Name + " - " + tracks.CurrentlyPlaying.TimeDuration().Round(time.Second).String(),
|
||||||
|
SpotifyItem: tracks.CurrentlyPlaying,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, track := range tracks.Items {
|
||||||
|
items = append(items, mainItem{
|
||||||
|
Name: track.Name,
|
||||||
|
Artist: track.Artists[0],
|
||||||
|
Duration: track.TimeDuration().Round(time.Second).String(),
|
||||||
|
ID: track.ID,
|
||||||
|
Desc: track.Artists[0].Name + " - " + track.TimeDuration().Round(time.Second).String(),
|
||||||
SpotifyItem: track,
|
SpotifyItem: track,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ArtistsView(ctx *gctx.Context, client *spotify.Client) ([]list.Item, error) {
|
func PlaylistView(ctx *gctx.Context, commands *commands.Commands, playlist spotify.SimplePlaylist) ([]list.Item, error) {
|
||||||
items := []list.Item{}
|
items := []list.Item{}
|
||||||
artists, err := commands.UserArtists(ctx, client, 1)
|
playlistItems, err := commands.PlaylistTracks(ctx, playlist.ID, 1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, item := range playlistItems.Items {
|
||||||
|
items = append(items, mainItem{
|
||||||
|
Name: item.Track.Track.Name,
|
||||||
|
Artist: item.Track.Track.Artists[0],
|
||||||
|
Duration: item.Track.Track.TimeDuration().Round(time.Second).String(),
|
||||||
|
ID: item.Track.Track.ID,
|
||||||
|
Desc: item.Track.Track.Artists[0].Name + " - " + item.Track.Track.TimeDuration().Round(time.Second).String(),
|
||||||
|
SpotifyItem: item,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ArtistsView(ctx *gctx.Context, commands *commands.Commands) ([]list.Item, error) {
|
||||||
|
items := []list.Item{}
|
||||||
|
artists, err := commands.UserArtists(ctx, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -57,54 +89,40 @@ func ArtistsView(ctx *gctx.Context, client *spotify.Client) ([]list.Item, error)
|
|||||||
items = append(items, mainItem{
|
items = append(items, mainItem{
|
||||||
Name: artist.Name,
|
Name: artist.Name,
|
||||||
ID: artist.ID,
|
ID: artist.ID,
|
||||||
Desc: fmt.Sprintf("%d followers, genres: %s, popularity: %d", artist.Followers.Count, artist.Genres, artist.Popularity),
|
Desc: fmt.Sprintf("%d followers", artist.Followers.Count),
|
||||||
SpotifyItem: artist.SimpleArtist,
|
SpotifyItem: artist.SimpleArtist,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SearchArtistsView(ctx *gctx.Context, client *spotify.Client, artists *spotify.FullArtistPage) ([]list.Item, error) {
|
func SearchArtistsView(ctx *gctx.Context, commands *commands.Commands, artists *spotify.FullArtistPage) ([]list.Item, error) {
|
||||||
items := []list.Item{}
|
items := []list.Item{}
|
||||||
for _, artist := range artists.Artists {
|
for _, artist := range artists.Artists {
|
||||||
items = append(items, mainItem{
|
items = append(items, mainItem{
|
||||||
Name: artist.Name,
|
Name: artist.Name,
|
||||||
ID: artist.ID,
|
ID: artist.ID,
|
||||||
Desc: fmt.Sprintf("%d followers, genres: %s, popularity: %d", artist.Followers.Count, artist.Genres, artist.Popularity),
|
Desc: fmt.Sprintf("%d followers", artist.Followers.Count),
|
||||||
SpotifyItem: artist.SimpleArtist,
|
SpotifyItem: artist.SimpleArtist,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SearchView(ctx *gctx.Context, client *spotify.Client, search string) ([]list.Item, *SearchResults, error) {
|
func SearchView(ctx *gctx.Context, commands *commands.Commands, search string) ([]list.Item, *SearchResults, error) {
|
||||||
items := []list.Item{}
|
items := []list.Item{}
|
||||||
|
|
||||||
result, err := commands.Search(ctx, client, search, 1)
|
result, err := commands.Search(ctx, search, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
items = append(items, mainItem{
|
items = append(
|
||||||
Name: "Tracks",
|
items,
|
||||||
Desc: "Search results",
|
mainItem{Name: "Tracks", Desc: "Search results", SpotifyItem: result.Tracks},
|
||||||
SpotifyItem: result.Tracks,
|
mainItem{Name: "Albums", Desc: "Search results", SpotifyItem: result.Albums},
|
||||||
})
|
mainItem{Name: "Artists", Desc: "Search results", SpotifyItem: result.Artists},
|
||||||
items = append(items, mainItem{
|
mainItem{Name: "Playlists", Desc: "Search results", SpotifyItem: result.Playlists},
|
||||||
Name: "Albums",
|
)
|
||||||
Desc: "Search results",
|
|
||||||
SpotifyItem: result.Albums,
|
|
||||||
})
|
|
||||||
items = append(items, mainItem{
|
|
||||||
Name: "Artists",
|
|
||||||
Desc: "Search results",
|
|
||||||
SpotifyItem: result.Artists,
|
|
||||||
})
|
|
||||||
|
|
||||||
items = append(items, mainItem{
|
|
||||||
Name: "Playlists",
|
|
||||||
Desc: "Search results",
|
|
||||||
SpotifyItem: result.Playlists,
|
|
||||||
})
|
|
||||||
results := &SearchResults{
|
results := &SearchResults{
|
||||||
Tracks: result.Tracks,
|
Tracks: result.Tracks,
|
||||||
Playlists: result.Playlists,
|
Playlists: result.Playlists,
|
||||||
@ -114,9 +132,9 @@ func SearchView(ctx *gctx.Context, client *spotify.Client, search string) ([]lis
|
|||||||
return items, results, nil
|
return items, results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AlbumsView(ctx *gctx.Context, client *spotify.Client) ([]list.Item, error) {
|
func AlbumsView(ctx *gctx.Context, commands *commands.Commands) ([]list.Item, error) {
|
||||||
items := []list.Item{}
|
items := []list.Item{}
|
||||||
albums, err := commands.UserAlbums(ctx, client, 1)
|
albums, err := commands.UserAlbums(ctx, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -124,41 +142,41 @@ func AlbumsView(ctx *gctx.Context, client *spotify.Client) ([]list.Item, error)
|
|||||||
items = append(items, mainItem{
|
items = append(items, mainItem{
|
||||||
Name: album.Name,
|
Name: album.Name,
|
||||||
ID: album.ID,
|
ID: album.ID,
|
||||||
Desc: fmt.Sprintf("%s, %d tracks", album.Artists[0].Name, album.Tracks.Total),
|
Desc: fmt.Sprintf("%s by %s, %d tracks, released %d", album.AlbumType, album.Artists[0].Name, album.Tracks.Total, album.ReleaseDateTime().Year()),
|
||||||
SpotifyItem: album.SimpleAlbum,
|
SpotifyItem: album.SimpleAlbum,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SearchPlaylistsView(ctx *gctx.Context, client *spotify.Client, playlists *spotify.SimplePlaylistPage) ([]list.Item, error) {
|
func SearchPlaylistsView(ctx *gctx.Context, commands *commands.Commands, playlists *spotify.SimplePlaylistPage) ([]list.Item, error) {
|
||||||
items := []list.Item{}
|
items := []list.Item{}
|
||||||
for _, playlist := range playlists.Playlists {
|
for _, playlist := range playlists.Playlists {
|
||||||
items = append(items, mainItem{
|
items = append(items, mainItem{
|
||||||
Name: playlist.Name,
|
Name: playlist.Name,
|
||||||
Desc: playlist.Description,
|
Desc: stripHtmlRegex(playlist.Description),
|
||||||
SpotifyItem: playlist,
|
SpotifyItem: playlist,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SearchAlbumsView(ctx *gctx.Context, client *spotify.Client, albums *spotify.SimpleAlbumPage) ([]list.Item, error) {
|
func SearchAlbumsView(ctx *gctx.Context, commands *commands.Commands, albums *spotify.SimpleAlbumPage) ([]list.Item, error) {
|
||||||
items := []list.Item{}
|
items := []list.Item{}
|
||||||
for _, album := range albums.Albums {
|
for _, album := range albums.Albums {
|
||||||
items = append(items, mainItem{
|
items = append(items, mainItem{
|
||||||
Name: album.Name,
|
Name: album.Name,
|
||||||
ID: album.ID,
|
ID: album.ID,
|
||||||
Desc: fmt.Sprintf("%s, %d", album.Artists[0].Name, album.ReleaseDateTime()),
|
Desc: fmt.Sprintf("%s by %s, released %d", album.AlbumType, album.Artists[0].Name, album.ReleaseDateTime().Year()),
|
||||||
SpotifyItem: album,
|
SpotifyItem: album,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ArtistAlbumsView(ctx *gctx.Context, album spotify.ID, client *spotify.Client) ([]list.Item, error) {
|
func ArtistAlbumsView(ctx *gctx.Context, album spotify.ID, commands *commands.Commands) ([]list.Item, error) {
|
||||||
items := []list.Item{}
|
items := []list.Item{}
|
||||||
albums, err := commands.ArtistAlbums(ctx, client, album, 1)
|
albums, err := commands.ArtistAlbums(ctx, album, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -173,9 +191,9 @@ func ArtistAlbumsView(ctx *gctx.Context, album spotify.ID, client *spotify.Clien
|
|||||||
return items, err
|
return items, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func AlbumTracksView(ctx *gctx.Context, album spotify.ID, client *spotify.Client) ([]list.Item, error) {
|
func AlbumTracksView(ctx *gctx.Context, album spotify.ID, commands *commands.Commands) ([]list.Item, error) {
|
||||||
items := []list.Item{}
|
items := []list.Item{}
|
||||||
tracks, err := commands.AlbumTracks(ctx, client, album, 1)
|
tracks, err := commands.AlbumTracks(ctx, album, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -192,7 +210,7 @@ func AlbumTracksView(ctx *gctx.Context, album spotify.ID, client *spotify.Client
|
|||||||
return items, err
|
return items, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func SearchTracksView(ctx *gctx.Context, client *spotify.Client, tracks *spotify.FullTrackPage) ([]list.Item, error) {
|
func SearchTracksView(ctx *gctx.Context, commands *commands.Commands, tracks *spotify.FullTrackPage) ([]list.Item, error) {
|
||||||
items := []list.Item{}
|
items := []list.Item{}
|
||||||
for _, track := range tracks.Tracks {
|
for _, track := range tracks.Tracks {
|
||||||
items = append(items, mainItem{
|
items = append(items, mainItem{
|
||||||
@ -207,9 +225,9 @@ func SearchTracksView(ctx *gctx.Context, client *spotify.Client, tracks *spotify
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SavedTracksView(ctx *gctx.Context, client *spotify.Client) ([]list.Item, error) {
|
func SavedTracksView(ctx *gctx.Context, commands *commands.Commands) ([]list.Item, error) {
|
||||||
items := []list.Item{}
|
items := []list.Item{}
|
||||||
tracks, err := commands.TrackList(ctx, client, 1)
|
tracks, err := commands.TrackList(ctx, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -226,58 +244,37 @@ func SavedTracksView(ctx *gctx.Context, client *spotify.Client) ([]list.Item, er
|
|||||||
return items, err
|
return items, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func MainView(ctx *gctx.Context, client *spotify.Client) ([]list.Item, error) {
|
func MainView(ctx *gctx.Context, commands *commands.Commands) ([]list.Item, error) {
|
||||||
var wg sync.WaitGroup
|
wg := errgroup.Group{}
|
||||||
var saved_items *spotify.SavedTrackPage
|
var saved_items *spotify.SavedTrackPage
|
||||||
var playlists *spotify.SimplePlaylistPage
|
var playlists *spotify.SimplePlaylistPage
|
||||||
var artists *spotify.FullArtistCursorPage
|
var artists *spotify.FullArtistCursorPage
|
||||||
var albums *spotify.SavedAlbumPage
|
var albums *spotify.SavedAlbumPage
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Go(func() (err error) {
|
||||||
go func() {
|
saved_items, err = commands.TrackList(ctx, 1)
|
||||||
defer wg.Done()
|
return
|
||||||
var err error
|
})
|
||||||
saved_items, err = commands.TrackList(ctx, client, 1)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Go(func() (err error) {
|
||||||
go func() {
|
playlists, err = commands.Playlists(ctx, 1)
|
||||||
defer wg.Done()
|
return
|
||||||
var err error
|
})
|
||||||
playlists, err = commands.Playlists(ctx, client, 1)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Go(func() (err error) {
|
||||||
go func() {
|
artists, err = commands.UserArtists(ctx, 1)
|
||||||
defer wg.Done()
|
return
|
||||||
var err error
|
})
|
||||||
artists, err = commands.UserArtists(ctx, client, 1)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Go(func() (err error) {
|
||||||
go func() {
|
albums, err = commands.UserAlbums(ctx, 1)
|
||||||
defer wg.Done()
|
return
|
||||||
var err error
|
})
|
||||||
albums, err = commands.UserAlbums(ctx, client, 1)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
wg.Wait()
|
err := wg.Wait()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
items := []list.Item{}
|
items := []list.Item{}
|
||||||
if saved_items != nil && saved_items.Total != 0 {
|
if saved_items != nil && saved_items.Total != 0 {
|
||||||
@ -301,14 +298,24 @@ func MainView(ctx *gctx.Context, client *spotify.Client) ([]list.Item, error) {
|
|||||||
SpotifyItem: artists,
|
SpotifyItem: artists,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
items = append(items, mainItem{
|
||||||
|
Name: "Queue",
|
||||||
|
Desc: "Your Current Queue",
|
||||||
|
SpotifyItem: spotify.Queue{},
|
||||||
|
})
|
||||||
if playlists != nil && playlists.Total != 0 {
|
if playlists != nil && playlists.Total != 0 {
|
||||||
for _, playlist := range playlists.Playlists {
|
for _, playlist := range playlists.Playlists {
|
||||||
items = append(items, mainItem{
|
items = append(items, mainItem{
|
||||||
Name: playlist.Name,
|
Name: playlist.Name,
|
||||||
Desc: playlist.Description,
|
Desc: stripHtmlRegex(playlist.Description),
|
||||||
SpotifyItem: playlist,
|
SpotifyItem: playlist,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func stripHtmlRegex(s string) string {
|
||||||
|
r := regexp.MustCompile(regex)
|
||||||
|
return r.ReplaceAllString(s, "")
|
||||||
|
}
|
||||||
|
115
src/youtube/youtube.go
Normal file
115
src/youtube/youtube.go
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
package youtube
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
"golang.org/x/oauth2/google"
|
||||||
|
"google.golang.org/api/option"
|
||||||
|
"google.golang.org/api/youtube/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getClient(ctx context.Context, config *oauth2.Config) *http.Client {
|
||||||
|
cacheFile, err := tokenCacheFile()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Unable to get path to cached credential file. %v", err)
|
||||||
|
}
|
||||||
|
tok, err := tokenFromFile(cacheFile)
|
||||||
|
if err != nil {
|
||||||
|
tok = getTokenFromWeb(config)
|
||||||
|
saveToken(cacheFile, tok)
|
||||||
|
}
|
||||||
|
return config.Client(ctx, tok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
|
||||||
|
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
|
||||||
|
fmt.Printf("Go to the following link in your browser then type the "+
|
||||||
|
"authorization code: \n%v\n", authURL)
|
||||||
|
|
||||||
|
var code string
|
||||||
|
if _, err := fmt.Scan(&code); err != nil {
|
||||||
|
log.Fatalf("Unable to read authorization code %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tok, err := config.Exchange(context.Background(), code)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Unable to retrieve token from web %v", err)
|
||||||
|
}
|
||||||
|
return tok
|
||||||
|
}
|
||||||
|
|
||||||
|
func tokenCacheFile() (string, error) {
|
||||||
|
usr, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
tokenCacheDir := filepath.Join(usr.HomeDir, ".credentials")
|
||||||
|
err = os.MkdirAll(tokenCacheDir, 0o700)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.Join(tokenCacheDir,
|
||||||
|
url.QueryEscape("youtube-go-quickstart.json")), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func tokenFromFile(file string) (*oauth2.Token, error) {
|
||||||
|
f, err := os.Open(file)
|
||||||
|
handleError(err, "Error opening file")
|
||||||
|
t := &oauth2.Token{}
|
||||||
|
err = json.NewDecoder(f).Decode(t)
|
||||||
|
defer f.Close()
|
||||||
|
return t, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveToken(file string, token *oauth2.Token) {
|
||||||
|
fmt.Printf("Saving credential file to: %s\n", file)
|
||||||
|
f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Unable to cache oauth token: %v", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
err = json.NewEncoder(f).Encode(token)
|
||||||
|
handleError(err, "Error encoding token")
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleError(err error, message string) {
|
||||||
|
if message == "" {
|
||||||
|
message = "Error making API call"
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf(message+": %v", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Search(query string) string {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
confDir, _ := os.UserConfigDir()
|
||||||
|
b, err := os.ReadFile(filepath.Join(confDir, "gospt", "client_secret.json"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Unable to read client secret file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := google.ConfigFromJSON(b, youtube.YoutubeReadonlyScope)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Unable to parse client secret file to config: %v", err)
|
||||||
|
}
|
||||||
|
client := getClient(ctx, config)
|
||||||
|
service, err := youtube.NewService(ctx, option.WithHTTPClient(client))
|
||||||
|
|
||||||
|
handleError(err, "Error creating YouTube client")
|
||||||
|
call := service.Search.List([]string{"snippet"})
|
||||||
|
call.Q(query)
|
||||||
|
response, err := call.Do()
|
||||||
|
handleError(err, "")
|
||||||
|
return fmt.Sprintf("https://www.youtube.com/watch?v=%s", response.Items[0].Id.VideoId)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user