Compare commits
67 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 |
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)"
|
@ -1,6 +1,6 @@
|
|||||||
gitea_urls:
|
gitea_urls:
|
||||||
api: https://gitea.asdf.cafe/api/v1
|
api: https://git.asdf.cafe/api/v1
|
||||||
download: https://gitea.asdf.cafe
|
download: https://git.asdf.cafe
|
||||||
skip_tls_verify: false
|
skip_tls_verify: false
|
||||||
|
|
||||||
before:
|
before:
|
||||||
@ -17,6 +17,8 @@ builds:
|
|||||||
ignore:
|
ignore:
|
||||||
- goos: windows
|
- goos: windows
|
||||||
goarch: "386"
|
goarch: "386"
|
||||||
|
ldflags:
|
||||||
|
- -s -w -X git.asdf.cafe/abs3nt/gospt/src.cmd.Version={{.Version}}
|
||||||
|
|
||||||
archives:
|
archives:
|
||||||
- format: tar.gz
|
- format: tar.gz
|
||||||
|
@ -1,117 +0,0 @@
|
|||||||
# .goreleaser.yaml
|
|
||||||
aurs:
|
|
||||||
-
|
|
||||||
# The package name.
|
|
||||||
#
|
|
||||||
# Defaults to the Project Name with a -bin suffix.
|
|
||||||
#
|
|
||||||
# Note that since this integration does not create a PKGBUILD to build from
|
|
||||||
# source, per Arch's guidelines.
|
|
||||||
# That said, GoReleaser will enforce a `-bin` suffix if its not present.
|
|
||||||
name: gospt-bin
|
|
||||||
|
|
||||||
# Template of your app's description.
|
|
||||||
# Default is empty.
|
|
||||||
description: "Spotify TUI And CLI written in Go"
|
|
||||||
|
|
||||||
# The maintainers of the package.
|
|
||||||
# Defaults to empty.
|
|
||||||
maintainers:
|
|
||||||
- 'abs3nt <abs3nt@asdf.cafe>'
|
|
||||||
|
|
||||||
# The contributors of the package.
|
|
||||||
# Defaults to empty.
|
|
||||||
contributors:
|
|
||||||
- 'abs3nt <abs3nt@asdf.cafe>'
|
|
||||||
|
|
||||||
# SPDX identifier of your app's license.
|
|
||||||
# Default is empty.
|
|
||||||
license: "GPL"
|
|
||||||
|
|
||||||
# The SSH private key that should be used to commit to the Git repository.
|
|
||||||
# This can either be a path or the key contents.
|
|
||||||
#
|
|
||||||
# IMPORTANT: the key must not be password-protected.
|
|
||||||
#
|
|
||||||
# WARNING: do not expose your private key in the configuration file!
|
|
||||||
private_key: '{{ .Env.AUR_KEY }}'
|
|
||||||
|
|
||||||
# The AUR Git URL for this package.
|
|
||||||
# Defaults to empty
|
|
||||||
# Publish is skipped if empty.
|
|
||||||
git_url: 'ssh://aur@aur.archlinux.org/gospt-bin.git'
|
|
||||||
|
|
||||||
# Setting this will prevent goreleaser to actually try to commit the updated
|
|
||||||
# formula - instead, the formula file will be stored on the dist folder only,
|
|
||||||
# leaving the responsibility of publishing it to the user.
|
|
||||||
#
|
|
||||||
# If set to auto, the release will not be uploaded to the AUR repo
|
|
||||||
# in case there is an indicator for prerelease in the tag e.g. v1.0.0-rc1.
|
|
||||||
#
|
|
||||||
# Default is false.
|
|
||||||
skip_upload: false
|
|
||||||
|
|
||||||
# List of additional packages that the software provides the features of.
|
|
||||||
#
|
|
||||||
# Defaults to the project name.
|
|
||||||
provides:
|
|
||||||
- gospt
|
|
||||||
|
|
||||||
# List of packages that conflict with, or cause problems with the package.
|
|
||||||
#
|
|
||||||
# Defaults to the project name.
|
|
||||||
conflicts:
|
|
||||||
- gospt
|
|
||||||
|
|
||||||
# List of packages that are not needed for the software to function,
|
|
||||||
# but provide additional features.
|
|
||||||
#
|
|
||||||
# Must be in the format `package: short description of the extra functionality`.
|
|
||||||
#
|
|
||||||
# Defaults to empty.
|
|
||||||
optdepends:
|
|
||||||
- 'spotifyd: for handling streaming'
|
|
||||||
|
|
||||||
# Custom package instructions.
|
|
||||||
#
|
|
||||||
# Defaults to `install -Dm755 "./PROJECT_NAME" "${pkgdir}/usr/bin/PROJECT_NAME",
|
|
||||||
# which is not always correct.
|
|
||||||
#
|
|
||||||
# We recommend you override this, installing the binary, license and
|
|
||||||
# everything else your package needs.
|
|
||||||
package: |-
|
|
||||||
# bin
|
|
||||||
install -Dm755 "./gospt" "${pkgdir}/usr/bin/gospt"
|
|
||||||
|
|
||||||
# license
|
|
||||||
install -Dm644 "./LICENSE.md" "${pkgdir}/usr/share/licenses/gospt/LICENSE"
|
|
||||||
|
|
||||||
# completions
|
|
||||||
mkdir -p "${pkgdir}/usr/share/bash-completion/completions/"
|
|
||||||
mkdir -p "${pkgdir}/usr/share/zsh/site-functions/"
|
|
||||||
mkdir -p "${pkgdir}/usr/share/fish/vendor_completions.d/"
|
|
||||||
install -Dm644 "./completions/gospt.bash" "${pkgdir}/usr/share/bash-completion/completions/gospt"
|
|
||||||
install -Dm644 "./completions/gospt.zsh" "${pkgdir}/usr/share/zsh/site-functions/_gospt"
|
|
||||||
install -Dm644 "./completions/gospt.fish" "${pkgdir}/usr/share/fish/vendor_completions.d/gospt.fish"
|
|
||||||
|
|
||||||
# Git author used to commit to the repository.
|
|
||||||
# Defaults are shown below.
|
|
||||||
commit_author:
|
|
||||||
name: abs3nt
|
|
||||||
email: abs3nt@asdf.cafe
|
|
||||||
|
|
||||||
# Commit message template.
|
|
||||||
# Defaults to `Update to {{ .Tag }}`.
|
|
||||||
commit_msg_template: "pkgbuild updates"
|
|
||||||
|
|
||||||
# If you build for multiple GOAMD64 versions, you may use this to choose which one to use.
|
|
||||||
# Defaults to `v1`.
|
|
||||||
goamd64: v2
|
|
||||||
|
|
||||||
# The value to be passed to `GIT_SSH_COMMAND`.
|
|
||||||
# This is mainly used to specify the SSH private key used to pull/push to
|
|
||||||
# the Git URL.
|
|
||||||
#
|
|
||||||
# Defaults to `ssh -i {{ .KeyPath }} -o StrictHostKeyChecking=accept-new -F /dev/null`.
|
|
||||||
git_ssh_command: 'ssh -i {{ .Env.KEY }} -o SomeOption=yes'
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
|||||||
settings:
|
|
||||||
files:
|
|
||||||
errors:
|
|
||||||
status: false
|
|
||||||
path: ""
|
|
||||||
name: .r.logs.log
|
|
||||||
legacy:
|
|
||||||
force: false
|
|
||||||
interval: 100ms
|
|
||||||
server:
|
|
||||||
status: true
|
|
||||||
open: false
|
|
||||||
port: 5002
|
|
||||||
host: ""
|
|
||||||
schema:
|
|
||||||
- name: /home/abs3nt/Dev/gospt
|
|
||||||
path: /home/abs3nt/Dev/gospt
|
|
||||||
commands:
|
|
||||||
tidy:
|
|
||||||
status: true
|
|
||||||
run:
|
|
||||||
status: true
|
|
||||||
watcher:
|
|
||||||
extensions:
|
|
||||||
- go
|
|
||||||
paths:
|
|
||||||
- /
|
|
@ -1,6 +1,6 @@
|
|||||||
pipeline:
|
steps:
|
||||||
build:
|
build:
|
||||||
image: golang:1.19
|
image: golang:1.22
|
||||||
commands:
|
commands:
|
||||||
- go mod tidy
|
- go mod tidy
|
||||||
- go build -o gospt
|
- go build -o gospt
|
||||||
@ -16,10 +16,3 @@ pipeline:
|
|||||||
secrets: [ gitea_token ]
|
secrets: [ gitea_token ]
|
||||||
when:
|
when:
|
||||||
event: tag
|
event: tag
|
||||||
publish_aur:
|
|
||||||
image: goreleaser/goreleaser
|
|
||||||
commands:
|
|
||||||
- goreleaser release --config .goreleaseraur.yaml --clean
|
|
||||||
secrets: [ gitea_token ]
|
|
||||||
when:
|
|
||||||
event: tag
|
|
||||||
|
16
README.md
16
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.
|
||||||
|
|
||||||
@ -14,15 +14,21 @@ This project is still under heavy development and some things might not work or
|
|||||||
## 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
|
||||||
@ -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)
|
||||||
|
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)
|
||||||
|
},
|
||||||
|
}
|
@ -19,7 +19,7 @@ var linkCmd = &cobra.Command{
|
|||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
fmt.Print(link)
|
fmt.Println(link)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -25,6 +25,6 @@ var nextCmd = &cobra.Command{
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return commands.Next(ctx, skipAmt)
|
return commands.Next(ctx, skipAmt, false)
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -13,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)
|
commands.NowPlaying(ctx, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -8,10 +8,10 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
cmds "gitea.asdf.cafe/abs3nt/gospt/src/commands"
|
cmds "git.asdf.cafe/abs3nt/gospt/src/commands"
|
||||||
|
|
||||||
"gitea.asdf.cafe/abs3nt/gospt/src/config"
|
"git.asdf.cafe/abs3nt/gospt/src/config"
|
||||||
"gitea.asdf.cafe/abs3nt/gospt/src/gctx"
|
"git.asdf.cafe/abs3nt/gospt/src/gctx"
|
||||||
"tuxpa.in/a/zlog"
|
"tuxpa.in/a/zlog"
|
||||||
|
|
||||||
"github.com/cristalhq/aconfig"
|
"github.com/cristalhq/aconfig"
|
||||||
@ -24,7 +24,6 @@ var (
|
|||||||
ctx *gctx.Context
|
ctx *gctx.Context
|
||||||
commands *cmds.Commands
|
commands *cmds.Commands
|
||||||
cfgFile string
|
cfgFile string
|
||||||
userLicense string
|
|
||||||
verbose bool
|
verbose bool
|
||||||
|
|
||||||
rootCmd = &cobra.Command{
|
rootCmd = &cobra.Command{
|
||||||
@ -90,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)
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gitea.asdf.cafe/abs3nt/gospt/src/tui"
|
"git.asdf.cafe/abs3nt/gospt/src/tui"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
@ -4,7 +4,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"gitea.asdf.cafe/abs3nt/gospt/src/tui"
|
"git.asdf.cafe/abs3nt/gospt/src/tui"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
@ -4,7 +4,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"gitea.asdf.cafe/abs3nt/gospt/src/tui"
|
"git.asdf.cafe/abs3nt/gospt/src/tui"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
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)
|
||||||
|
}
|
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)
|
||||||
|
}
|
110
go.mod
110
go.mod
@ -1,63 +1,83 @@
|
|||||||
module gitea.asdf.cafe/abs3nt/gospt
|
module git.asdf.cafe/abs3nt/gospt
|
||||||
|
|
||||||
go 1.19
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
gfx.cafe/util/go/frand v0.0.0-20230121041905-80dafb1e973e
|
|
||||||
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
|
||||||
modernc.org/sqlite v1.20.4
|
golang.org/x/oauth2 v0.21.0
|
||||||
tuxpa.in/a/zlog v1.60.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/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
cloud.google.com/go/auth v0.7.0 // indirect
|
||||||
github.com/aymanbagabas/go-osc52 v1.0.3 // 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/dustin/go-humanize v1.0.0 // indirect
|
github.com/charmbracelet/x/input v0.1.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/charmbracelet/x/term v0.1.1 // indirect
|
||||||
github.com/google/uuid v1.3.0 // 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/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // 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-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.16 // 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/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/rs/zerolog v1.28.0 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/sahilm/fuzzy v0.1.0 // 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/mod v0.3.0 // indirect
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
golang.org/x/sys v0.1.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||||
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78 // indirect
|
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/crypto v0.25.0 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
golang.org/x/sys v0.22.0 // indirect
|
||||||
google.golang.org/protobuf v1.27.1 // 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
|
||||||
lukechampine.com/uint128 v1.2.0 // indirect
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||||
modernc.org/cc/v3 v3.40.0 // indirect
|
modernc.org/libc v1.52.1 // indirect
|
||||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
modernc.org/mathutil v1.6.0 // indirect
|
||||||
modernc.org/libc v1.22.2 // indirect
|
modernc.org/memory v1.8.0 // indirect
|
||||||
modernc.org/mathutil v1.5.0 // indirect
|
modernc.org/strutil v1.2.0 // indirect
|
||||||
modernc.org/memory v1.4.0 // indirect
|
modernc.org/token v1.1.0 // indirect
|
||||||
modernc.org/opt v0.1.3 // indirect
|
|
||||||
modernc.org/strutil v1.1.3 // indirect
|
|
||||||
modernc.org/token v1.0.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
361
go.sum
361
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=
|
||||||
@ -31,58 +48,86 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
|
|||||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
gfx.cafe/util/go/frand v0.0.0-20230121041905-80dafb1e973e h1:A62zlsu3HkEAVRIb+cCpRIpSTmd047+ABV1KC2RsI2U=
|
|
||||||
gfx.cafe/util/go/frand v0.0.0-20230121041905-80dafb1e973e/go.mod h1:LNHxMJl0WnIr5+OChYxlVopxk+j7qxZv0XvWCzB6uGE=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
|
||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
|
||||||
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/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
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.4.0/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.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
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/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=
|
||||||
@ -103,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=
|
||||||
@ -115,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.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.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
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=
|
||||||
@ -126,22 +177,33 @@ 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-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
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/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
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=
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
@ -156,68 +218,95 @@ github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb
|
|||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
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-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/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
|
|
||||||
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/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-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
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.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
|
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.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/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 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/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/zmb3/spotify/v2 v2.3.1 h1:aEyIPotROM3JJjHMCImFROgnPIUpzVo8wymYSaPSd9w=
|
github.com/zmb3/spotify/v2 v2.4.2 h1:j3yNN5lKVEMZQItJF4MHCSZbfNWmXO+KaC+3RFaLlLc=
|
||||||
github.com/zmb3/spotify/v2 v2.3.1/go.mod h1:+LVh9CafHu7SedyqYmEf12Rd01dIVlEL845yNhksW0E=
|
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=
|
||||||
@ -247,8 +336,11 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
|||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
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 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
|
||||||
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=
|
||||||
@ -275,16 +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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
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=
|
||||||
@ -293,7 +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-20201020160332-67f06af15bc9/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=
|
||||||
@ -321,28 +430,51 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
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-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-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/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-20220520151302-bc2c85ada10a/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-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.0.0-20220915200043-7b5979e65e41/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
|
||||||
golang.org/x/sys v0.1.0/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=
|
||||||
@ -386,12 +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.0.0-20201124115921-2c860bdd6e78 h1:M8tBwCtWD/cZV9DZpFYRUgaymAYAr+aIUTWzDaM3uPs=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
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=
|
||||||
@ -409,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=
|
||||||
@ -446,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=
|
||||||
@ -458,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=
|
||||||
@ -470,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=
|
||||||
@ -487,32 +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=
|
||||||
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
modernc.org/cc/v4 v4.21.2 h1:dycHFB/jDc3IyacKipCNSDrjIC0Lm1hyoWOZTRR20Lk=
|
||||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
modernc.org/cc/v4 v4.21.2/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
|
||||||
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
|
modernc.org/ccgo/v4 v4.17.10 h1:6wrtRozgrhCxieCeJh85QsxkX/2FFrT9hdaWPlbn4Zo=
|
||||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
modernc.org/ccgo/v4 v4.17.10/go.mod h1:0NBHgsqTTpm9cA5z2ccErvGZmtntSM9qD2kFAs6pjXM=
|
||||||
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
|
||||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
|
||||||
modernc.org/libc v1.22.2 h1:4U7v51GyhlWqQmwCHj28Rdq2Yzwk55ovjFrdPjs8Hb0=
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
||||||
modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug=
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
modernc.org/libc v1.52.1 h1:uau0VoiT5hnR+SpoWekCKbLqm7v6dhRL3hI+NQhgN3M=
|
||||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
modernc.org/libc v1.52.1/go.mod h1:HR4nVzFDSDizP620zcMCgjb1/8xk2lg5p/8yjfGv1IQ=
|
||||||
modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk=
|
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||||
modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
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 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||||
modernc.org/sqlite v1.20.4 h1:J8+m2trkN+KKoE7jglyHYYYiaq5xmz2HoHJIiBlRzbE=
|
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||||
modernc.org/sqlite v1.20.4/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A=
|
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
|
||||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
modernc.org/sqlite v1.30.1 h1:YFhPVfu2iIgUf9kuA1CR7iiHdcEEsI2i+yjRYHscyxk=
|
||||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
modernc.org/sqlite v1.30.1/go.mod h1:DUmsiWQDaAvU4abhc/N+djlom/L2o8f7gZ95RCvyoLU=
|
||||||
modernc.org/tcl v1.15.0 h1:oY+JeD11qVVSgVvodMJsu7Edf8tr5E/7tuhF5cNYz34=
|
modernc.org/sqlite v1.30.2 h1:IPVVkhLu5mMVnS1dQgh3h0SAACRWcVk7aoLP9Us3UCk=
|
||||||
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
modernc.org/sqlite v1.30.2/go.mod h1:DUmsiWQDaAvU4abhc/N+djlom/L2o8f7gZ95RCvyoLU=
|
||||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||||
modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE=
|
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.60.0 h1:bU4wJk6nwvaFsKIvKKxgGM0uO+Z2kaE8LzgRiQ4NCRw=
|
tuxpa.in/a/zlog v1.61.0 h1:7wrS6G4QwpnOmgHRQknrr7IgiMXrfGpekkU0PjM9FhE=
|
||||||
tuxpa.in/a/zlog v1.60.0/go.mod h1:1t8SX1a4zLy+p6ylGn6m1ZXnssTPr/2ErdPjjSP+C2k=
|
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 (
|
||||||
"gitea.asdf.cafe/abs3nt/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"
|
||||||
|
|
||||||
"gitea.asdf.cafe/abs3nt/gospt/src/config"
|
"tuxpa.in/a/zlog/log"
|
||||||
"gitea.asdf.cafe/abs3nt/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 {
|
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")
|
||||||
}
|
|
||||||
}
|
|
||||||
out, err := json.MarshalIndent(tok, "", " ")
|
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
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())
|
||||||
})
|
})
|
||||||
go func() {
|
server := &http.Server{
|
||||||
err := http.ListenAndServe(fmt.Sprintf(":%s", config.Values.Port), nil)
|
Addr: fmt.Sprintf(":%s", config.Values.Port),
|
||||||
if err != nil {
|
ReadHeaderTimeout: 5 * time.Second,
|
||||||
log.Fatal(err)
|
|
||||||
}
|
}
|
||||||
|
go func() {
|
||||||
|
server.ListenAndServe()
|
||||||
}()
|
}()
|
||||||
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")
|
||||||
}
|
}
|
||||||
|
2
src/cache/cache.go
vendored
2
src/cache/cache.go
vendored
@ -42,7 +42,7 @@ func (c *Cache) save(m map[string]CacheEntry) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Trace().Str("tosave", string(payload)).Msg("saving cache")
|
log.Trace().Str("tosave", string(payload)).Msg("saving cache")
|
||||||
err = os.WriteFile(c.Root, payload, 0640)
|
err = os.WriteFile(c.Root, payload, 0o600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
"math/rand"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -14,13 +15,13 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gfx.cafe/util/go/frand"
|
|
||||||
"gitea.asdf.cafe/abs3nt/gospt/src/auth"
|
|
||||||
"gitea.asdf.cafe/abs3nt/gospt/src/cache"
|
|
||||||
"gitea.asdf.cafe/abs3nt/gospt/src/gctx"
|
|
||||||
|
|
||||||
"github.com/zmb3/spotify/v2"
|
"github.com/zmb3/spotify/v2"
|
||||||
_ "modernc.org/sqlite"
|
_ "modernc.org/sqlite"
|
||||||
|
|
||||||
|
"git.asdf.cafe/abs3nt/gospt/src/auth"
|
||||||
|
"git.asdf.cafe/abs3nt/gospt/src/cache"
|
||||||
|
"git.asdf.cafe/abs3nt/gospt/src/gctx"
|
||||||
|
"git.asdf.cafe/abs3nt/gospt/src/youtube"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Commands struct {
|
type Commands struct {
|
||||||
@ -147,7 +148,8 @@ func (c *Commands) UserArtists(ctx *gctx.Context, page int) (*spotify.FullArtist
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) ArtistAlbums(ctx *gctx.Context, artist spotify.ID, page int) (*spotify.SimpleAlbumPage, error) {
|
func (c *Commands) ArtistAlbums(ctx *gctx.Context, artist spotify.ID, page int) (*spotify.SimpleAlbumPage, error) {
|
||||||
albums, err := c.Client().GetArtistAlbums(ctx, artist, []spotify.AlbumType{1, 2, 3, 4}, spotify.Market(spotify.CountryUSA), spotify.Limit(50), spotify.Offset((page-1)*50))
|
albums, err := c.Client().
|
||||||
|
GetArtistAlbums(ctx, artist, []spotify.AlbumType{1, 2, 3, 4}, spotify.Market(spotify.CountryUSA), spotify.Limit(50), spotify.Offset((page-1)*50))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -155,7 +157,8 @@ func (c *Commands) ArtistAlbums(ctx *gctx.Context, artist spotify.ID, page int)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) Search(ctx *gctx.Context, search string, page int) (*spotify.SearchResult, error) {
|
func (c *Commands) Search(ctx *gctx.Context, search string, page int) (*spotify.SearchResult, error) {
|
||||||
result, err := c.Client().Search(ctx, search, spotify.SearchTypeAlbum|spotify.SearchTypeArtist|spotify.SearchTypeTrack|spotify.SearchTypePlaylist, spotify.Limit(50), spotify.Offset((page-1)*50))
|
result, err := c.Client().
|
||||||
|
Search(ctx, search, spotify.SearchTypeAlbum|spotify.SearchTypeArtist|spotify.SearchTypeTrack|spotify.SearchTypePlaylist, spotify.Limit(50), spotify.Offset((page-1)*50))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -163,7 +166,8 @@ func (c *Commands) Search(ctx *gctx.Context, search string, page int) (*spotify.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) AlbumTracks(ctx *gctx.Context, album spotify.ID, page int) (*spotify.SimpleTrackPage, error) {
|
func (c *Commands) AlbumTracks(ctx *gctx.Context, album spotify.ID, page int) (*spotify.SimpleTrackPage, error) {
|
||||||
tracks, err := c.Client().GetAlbumTracks(ctx, album, spotify.Limit(50), spotify.Offset((page-1)*50), spotify.Market(spotify.CountryUSA))
|
tracks, err := c.Client().
|
||||||
|
GetAlbumTracks(ctx, album, spotify.Limit(50), spotify.Offset((page-1)*50), spotify.Market(spotify.CountryUSA))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -171,11 +175,11 @@ func (c *Commands) AlbumTracks(ctx *gctx.Context, album spotify.ID, page int) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) UserAlbums(ctx *gctx.Context, page int) (*spotify.SavedAlbumPage, error) {
|
func (c *Commands) UserAlbums(ctx *gctx.Context, page int) (*spotify.SavedAlbumPage, error) {
|
||||||
albums, err := c.Client().CurrentUsersAlbums(ctx, spotify.Limit(50), spotify.Offset((page-1)*50))
|
return c.Client().CurrentUsersAlbums(ctx, spotify.Limit(50), spotify.Offset((page-1)*50))
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
|
||||||
}
|
func (c *Commands) UserQueue(ctx *gctx.Context) (*spotify.Queue, error) {
|
||||||
return albums, nil
|
return c.Client().GetQueue(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) PlayUrl(ctx *gctx.Context, args []string) error {
|
func (c *Commands) PlayUrl(ctx *gctx.Context, args []string) error {
|
||||||
@ -237,7 +241,7 @@ func (c *Commands) QueueSong(ctx *gctx.Context, id spotify.ID) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) PlaySongInPlaylist(ctx *gctx.Context, context *spotify.URI, offset int) error {
|
func (c *Commands) PlaySongInPlaylist(ctx *gctx.Context, context *spotify.URI, offset *int) error {
|
||||||
e := c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
e := c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
||||||
PlaybackOffset: &spotify.PlaybackOffset{Position: offset},
|
PlaybackOffset: &spotify.PlaybackOffset{Position: offset},
|
||||||
PlaybackContext: context,
|
PlaybackContext: context,
|
||||||
@ -253,7 +257,6 @@ func (c *Commands) PlaySongInPlaylist(ctx *gctx.Context, context *spotify.URI, o
|
|||||||
PlaybackContext: context,
|
PlaybackContext: context,
|
||||||
DeviceID: &deviceID,
|
DeviceID: &deviceID,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isNoActiveError(err) {
|
if isNoActiveError(err) {
|
||||||
deviceID, err := c.activateDevice(ctx)
|
deviceID, err := c.activateDevice(ctx)
|
||||||
@ -304,9 +307,6 @@ func (c *Commands) PlayLikedSongs(ctx *gctx.Context, position int) error {
|
|||||||
}
|
}
|
||||||
err = c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
err = c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
||||||
PlaybackContext: &playlist.URI,
|
PlaybackContext: &playlist.URI,
|
||||||
PlaybackOffset: &spotify.PlaybackOffset{
|
|
||||||
Position: 0,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isNoActiveError(err) {
|
if isNoActiveError(err) {
|
||||||
@ -316,9 +316,6 @@ func (c *Commands) PlayLikedSongs(ctx *gctx.Context, position int) error {
|
|||||||
}
|
}
|
||||||
err = c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
err = c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
||||||
PlaybackContext: &playlist.URI,
|
PlaybackContext: &playlist.URI,
|
||||||
PlaybackOffset: &spotify.PlaybackOffset{
|
|
||||||
Position: 0,
|
|
||||||
},
|
|
||||||
DeviceID: &deviceID,
|
DeviceID: &deviceID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -381,16 +378,13 @@ func (c *Commands) RadioGivenArtist(ctx *gctx.Context, artist spotify.SimpleArti
|
|||||||
}
|
}
|
||||||
c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
||||||
PlaybackContext: &radioPlaylist.URI,
|
PlaybackContext: &radioPlaylist.URI,
|
||||||
PlaybackOffset: &spotify.PlaybackOffset{
|
|
||||||
Position: 0,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
err = c.Client().Repeat(ctx, "context")
|
err = c.Client().Repeat(ctx, "context")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
id := frand.Intn(len(recomendationIds)-2) + 1
|
id := rand.Intn(len(recomendationIds)-2) + 1
|
||||||
seed := spotify.Seeds{
|
seed := spotify.Seeds{
|
||||||
Tracks: []spotify.ID{recomendationIds[id]},
|
Tracks: []spotify.ID{recomendationIds[id]},
|
||||||
}
|
}
|
||||||
@ -469,9 +463,6 @@ func (c *Commands) RadioGivenSong(ctx *gctx.Context, song spotify.SimpleTrack, p
|
|||||||
}
|
}
|
||||||
err = c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
err = c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
||||||
PlaybackContext: &radioPlaylist.URI,
|
PlaybackContext: &radioPlaylist.URI,
|
||||||
PlaybackOffset: &spotify.PlaybackOffset{
|
|
||||||
Position: 0,
|
|
||||||
},
|
|
||||||
PositionMs: pos,
|
PositionMs: pos,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -482,9 +473,6 @@ func (c *Commands) RadioGivenSong(ctx *gctx.Context, song spotify.SimpleTrack, p
|
|||||||
}
|
}
|
||||||
err = c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
err = c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
||||||
PlaybackContext: &radioPlaylist.URI,
|
PlaybackContext: &radioPlaylist.URI,
|
||||||
PlaybackOffset: &spotify.PlaybackOffset{
|
|
||||||
Position: 0,
|
|
||||||
},
|
|
||||||
DeviceID: &deviceID,
|
DeviceID: &deviceID,
|
||||||
PositionMs: pos,
|
PositionMs: pos,
|
||||||
})
|
})
|
||||||
@ -498,7 +486,7 @@ func (c *Commands) RadioGivenSong(ctx *gctx.Context, song spotify.SimpleTrack, p
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
id := frand.Intn(len(recomendationIds)-2) + 1
|
id := rand.Intn(len(recomendationIds)-2) + 1
|
||||||
seed := spotify.Seeds{
|
seed := spotify.Seeds{
|
||||||
Tracks: []spotify.ID{recomendationIds[id]},
|
Tracks: []spotify.ID{recomendationIds[id]},
|
||||||
}
|
}
|
||||||
@ -528,10 +516,18 @@ func (c *Commands) RadioGivenSong(ctx *gctx.Context, song spotify.SimpleTrack, p
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Commands) DeleteTracksFromPlaylist(ctx *gctx.Context, tracks []spotify.ID, playlist spotify.ID) error {
|
||||||
|
_, err := c.Client().RemoveTracksFromPlaylist(ctx, playlist, tracks...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Commands) SongExists(db *sql.DB, song spotify.ID) (bool, error) {
|
func (c *Commands) SongExists(db *sql.DB, song spotify.ID) (bool, error) {
|
||||||
song_id := string(song)
|
song_id := string(song)
|
||||||
sqlStmt := `SELECT id FROM radio WHERE id = ?`
|
sqlStmt := `SELECT id FROM radio WHERE id = ?`
|
||||||
err := db.QueryRow(sqlStmt, string(song_id)).Scan(&song_id)
|
err := db.QueryRow(sqlStmt, song_id).Scan(&song_id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != sql.ErrNoRows {
|
if err != sql.ErrNoRows {
|
||||||
return false, err
|
return false, err
|
||||||
@ -562,16 +558,13 @@ func (c *Commands) Radio(ctx *gctx.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
seed_song = tracks.Tracks[frand.Intn(len(tracks.Tracks))].SimpleTrack
|
seed_song = tracks.Tracks[rand.Intn(len(tracks.Tracks))].SimpleTrack
|
||||||
} else {
|
} else if !current_song.Playing {
|
||||||
if !current_song.Playing {
|
|
||||||
|
|
||||||
tracks, err := c.Client().CurrentUsersTracks(ctx, spotify.Limit(10))
|
tracks, err := c.Client().CurrentUsersTracks(ctx, spotify.Limit(10))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
seed_song = tracks.Tracks[frand.Intn(len(tracks.Tracks))].SimpleTrack
|
seed_song = tracks.Tracks[rand.Intn(len(tracks.Tracks))].SimpleTrack
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return c.RadioGivenSong(ctx, seed_song, current_song.Progress)
|
return c.RadioGivenSong(ctx, seed_song, current_song.Progress)
|
||||||
}
|
}
|
||||||
@ -587,6 +580,7 @@ func (c *Commands) RefillRadio(ctx *gctx.Context) error {
|
|||||||
to_remove := []spotify.ID{}
|
to_remove := []spotify.ID{}
|
||||||
radioPlaylist, db, err := c.GetRadioPlaylist(ctx, "")
|
radioPlaylist, db, err := c.GetRadioPlaylist(ctx, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if status.PlaybackContext.URI != radioPlaylist.URI {
|
if status.PlaybackContext.URI != radioPlaylist.URI {
|
||||||
@ -598,20 +592,17 @@ func (c *Commands) RefillRadio(ctx *gctx.Context) error {
|
|||||||
return fmt.Errorf("orig playlist items: %w", err)
|
return fmt.Errorf("orig playlist items: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
found := false
|
|
||||||
page := 0
|
page := 0
|
||||||
for !found {
|
for {
|
||||||
tracks, err := c.Client().GetPlaylistItems(ctx, radioPlaylist.ID, spotify.Limit(50), spotify.Offset(page*50))
|
tracks, err := c.Client().GetPlaylistItems(ctx, radioPlaylist.ID, spotify.Limit(50), spotify.Offset(page*50))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("tracks: %w", err)
|
return fmt.Errorf("tracks: %w", err)
|
||||||
}
|
}
|
||||||
if len(tracks.Items) == 0 {
|
if len(tracks.Items) == 0 {
|
||||||
found = true
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
for _, track := range tracks.Items {
|
for _, track := range tracks.Items {
|
||||||
if track.Track.Track.ID == status.Item.ID {
|
if track.Track.Track.ID == status.Item.ID {
|
||||||
found = true
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
to_remove = append(to_remove, track.Track.Track.ID)
|
to_remove = append(to_remove, track.Track.Track.ID)
|
||||||
@ -630,7 +621,7 @@ func (c *Commands) RefillRadio(ctx *gctx.Context) error {
|
|||||||
return fmt.Errorf("error clearing playlist: %w", err)
|
return fmt.Errorf("error clearing playlist: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err = c.Client().RemoveTracksFromPlaylist(ctx, radioPlaylist.ID, trackGroups...)
|
c.Client().RemoveTracksFromPlaylist(ctx, radioPlaylist.ID, trackGroups...)
|
||||||
}
|
}
|
||||||
|
|
||||||
to_add := 500 - (playlistItems.Total - len(to_remove))
|
to_add := 500 - (playlistItems.Total - len(to_remove))
|
||||||
@ -642,14 +633,15 @@ func (c *Commands) RefillRadio(ctx *gctx.Context) error {
|
|||||||
pages := int(math.Ceil(float64(total) / 50))
|
pages := int(math.Ceil(float64(total) / 50))
|
||||||
randomPage := 1
|
randomPage := 1
|
||||||
if pages > 1 {
|
if pages > 1 {
|
||||||
randomPage = frand.Intn(int(pages-1)) + 1
|
randomPage = rand.Intn(pages-1) + 1
|
||||||
}
|
}
|
||||||
playlistPage, err := c.Client().GetPlaylistItems(ctx, radioPlaylist.ID, spotify.Limit(50), spotify.Offset((randomPage-1)*50))
|
playlistPage, err := c.Client().
|
||||||
|
GetPlaylistItems(ctx, radioPlaylist.ID, spotify.Limit(50), spotify.Offset((randomPage-1)*50))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("playlist page: %w", err)
|
return fmt.Errorf("playlist page: %w", err)
|
||||||
}
|
}
|
||||||
pageSongs := playlistPage.Items
|
pageSongs := playlistPage.Items
|
||||||
frand.Shuffle(len(pageSongs), func(i, j int) { pageSongs[i], pageSongs[j] = pageSongs[j], pageSongs[i] })
|
rand.Shuffle(len(pageSongs), func(i, j int) { pageSongs[i], pageSongs[j] = pageSongs[j], pageSongs[i] })
|
||||||
seedCount := 5
|
seedCount := 5
|
||||||
if len(pageSongs) < seedCount {
|
if len(pageSongs) < seedCount {
|
||||||
seedCount = len(pageSongs)
|
seedCount = len(pageSongs)
|
||||||
@ -684,6 +676,9 @@ func (c *Commands) RefillRadio(ctx *gctx.Context) error {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
_, err = db.QueryContext(ctx, fmt.Sprintf("INSERT INTO radio (id) VALUES('%s')", rec.String()))
|
_, err = db.QueryContext(ctx, fmt.Sprintf("INSERT INTO radio (id) VALUES('%s')", rec.String()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
queue = append(queue, rec)
|
queue = append(queue, rec)
|
||||||
}
|
}
|
||||||
to_add -= len(queue)
|
to_add -= len(queue)
|
||||||
@ -696,7 +691,7 @@ func (c *Commands) RefillRadio(ctx *gctx.Context) error {
|
|||||||
return fmt.Errorf("repeat: %w", err)
|
return fmt.Errorf("repeat: %w", err)
|
||||||
}
|
}
|
||||||
for to_add > 0 {
|
for to_add > 0 {
|
||||||
id := frand.Intn(len(recomendationIds)-2) + 1
|
id := rand.Intn(len(recomendationIds)-2) + 1
|
||||||
seed := spotify.Seeds{
|
seed := spotify.Seeds{
|
||||||
Tracks: []spotify.ID{recomendationIds[id]},
|
Tracks: []spotify.ID{recomendationIds[id]},
|
||||||
}
|
}
|
||||||
@ -762,7 +757,7 @@ func (c *Commands) Pause(ctx *gctx.Context) error {
|
|||||||
func (c *Commands) TogglePlay(ctx *gctx.Context) error {
|
func (c *Commands) TogglePlay(ctx *gctx.Context) error {
|
||||||
current, err := c.Client().PlayerCurrentlyPlaying(ctx)
|
current, err := c.Client().PlayerCurrentlyPlaying(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return c.Play(ctx)
|
||||||
}
|
}
|
||||||
if !current.Playing {
|
if !current.Playing {
|
||||||
return c.Play(ctx)
|
return c.Play(ctx)
|
||||||
@ -794,7 +789,13 @@ func (c *Commands) Unlike(ctx *gctx.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) Next(ctx *gctx.Context, amt int) error {
|
func (c *Commands) Next(ctx *gctx.Context, amt int, inqueue bool) error {
|
||||||
|
if inqueue {
|
||||||
|
for i := 0; i < amt; i++ {
|
||||||
|
c.Client().Next(ctx)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if amt == 1 {
|
if amt == 1 {
|
||||||
err := c.Client().Next(ctx)
|
err := c.Client().Next(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -825,9 +826,10 @@ func (c *Commands) Next(ctx *gctx.Context, amt int) error {
|
|||||||
case "playlist":
|
case "playlist":
|
||||||
found := false
|
found := false
|
||||||
currentTrackIndex := 0
|
currentTrackIndex := 0
|
||||||
for !found {
|
|
||||||
page := 1
|
page := 1
|
||||||
playlist, err := c.Client().GetPlaylistItems(ctx, spotify.ID(strings.Split(string(current.PlaybackContext.URI), ":")[2]), spotify.Limit(50), spotify.Offset((page-1)*50))
|
for !found {
|
||||||
|
playlist, err := c.Client().
|
||||||
|
GetPlaylistItems(ctx, spotify.ID(strings.Split(string(current.PlaybackContext.URI), ":")[2]), spotify.Limit(50), spotify.Offset((page-1)*50))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -840,19 +842,21 @@ func (c *Commands) Next(ctx *gctx.Context, amt int) error {
|
|||||||
}
|
}
|
||||||
page++
|
page++
|
||||||
}
|
}
|
||||||
|
pos := currentTrackIndex + amt
|
||||||
c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
||||||
PlaybackContext: ¤t.PlaybackContext.URI,
|
PlaybackContext: ¤t.PlaybackContext.URI,
|
||||||
PlaybackOffset: &spotify.PlaybackOffset{
|
PlaybackOffset: &spotify.PlaybackOffset{
|
||||||
Position: currentTrackIndex + amt,
|
Position: &pos,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
case "album":
|
case "album":
|
||||||
found := false
|
found := false
|
||||||
currentTrackIndex := 0
|
currentTrackIndex := 0
|
||||||
for !found {
|
|
||||||
page := 1
|
page := 1
|
||||||
playlist, err := c.Client().GetAlbumTracks(ctx, spotify.ID(strings.Split(string(current.PlaybackContext.URI), ":")[2]), spotify.Limit(50), spotify.Offset((page-1)*50))
|
for !found {
|
||||||
|
playlist, err := c.Client().
|
||||||
|
GetAlbumTracks(ctx, spotify.ID(strings.Split(string(current.PlaybackContext.URI), ":")[2]), spotify.Limit(50), spotify.Offset((page-1)*50))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -865,10 +869,11 @@ func (c *Commands) Next(ctx *gctx.Context, amt int) error {
|
|||||||
}
|
}
|
||||||
page++
|
page++
|
||||||
}
|
}
|
||||||
|
pos := currentTrackIndex + amt
|
||||||
c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
||||||
PlaybackContext: ¤t.PlaybackContext.URI,
|
PlaybackContext: ¤t.PlaybackContext.URI,
|
||||||
PlaybackOffset: &spotify.PlaybackOffset{
|
PlaybackOffset: &spotify.PlaybackOffset{
|
||||||
Position: currentTrackIndex + amt,
|
Position: &pos,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
@ -903,10 +908,24 @@ func (c *Commands) Status(ctx *gctx.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
fmt.Println(state)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Commands) DownloadCover(ctx *gctx.Context, args []string) error {
|
||||||
|
destinationPath := filepath.Clean(args[0])
|
||||||
|
state, err := c.Client().PlayerState(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f, err := os.OpenFile(destinationPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = state.Item.Album.Images[0].Download(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println(state)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -918,15 +937,35 @@ func (c *Commands) Link(ctx *gctx.Context) (string, error) {
|
|||||||
return state.Item.ExternalURLs["spotify"], nil
|
return state.Item.ExternalURLs["spotify"], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Commands) YoutubeLink(ctx *gctx.Context) (string, error) {
|
||||||
|
state, err := c.Client().PlayerState(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
link := youtube.Search(state.Item.Artists[0].Name + state.Item.Name)
|
||||||
|
return link, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Commands) LinkContext(ctx *gctx.Context) (string, error) {
|
func (c *Commands) LinkContext(ctx *gctx.Context) (string, error) {
|
||||||
state, err := c.Client().PlayerState(ctx)
|
state, err := c.Client().PlayerState(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return string(state.PlaybackContext.ExternalURLs["spotify"]), nil
|
return state.PlaybackContext.ExternalURLs["spotify"], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) NowPlaying(ctx *gctx.Context) error {
|
func (c *Commands) NowPlaying(ctx *gctx.Context, args []string) error {
|
||||||
|
if len(args) > 0 {
|
||||||
|
if args[0] == "force" {
|
||||||
|
current, err := c.Client().PlayerCurrentlyPlaying(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
str := FormatSong(current)
|
||||||
|
fmt.Println(str)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
song, err := cache.DefaultCache().GetOrDo("now_playing", func() (string, error) {
|
song, err := cache.DefaultCache().GetOrDo("now_playing", func() (string, error) {
|
||||||
current, err := c.Client().PlayerCurrentlyPlaying(ctx)
|
current, err := c.Client().PlayerCurrentlyPlaying(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -943,17 +982,25 @@ func (c *Commands) NowPlaying(ctx *gctx.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func FormatSong(current *spotify.CurrentlyPlaying) string {
|
func FormatSong(current *spotify.CurrentlyPlaying) string {
|
||||||
icon := "▶"
|
out := "▶"
|
||||||
if !current.Playing {
|
if !current.Playing {
|
||||||
icon = "⏸"
|
out = "⏸"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s %s - %s", icon, current.Item.Name, current.Item.Artists[0].Name)
|
if current != nil {
|
||||||
|
if current.Item != nil {
|
||||||
|
out += fmt.Sprintf(" %s", current.Item.Name)
|
||||||
|
if len(current.Item.Artists) > 0 {
|
||||||
|
out += fmt.Sprintf(" - %s", current.Item.Artists[0].Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) Shuffle(ctx *gctx.Context) error {
|
func (c *Commands) Shuffle(ctx *gctx.Context) error {
|
||||||
state, err := c.Client().PlayerState(ctx)
|
state, err := c.Client().PlayerState(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to get current playstate")
|
return fmt.Errorf("failed to get current playstate")
|
||||||
}
|
}
|
||||||
err = c.Client().Shuffle(ctx, !state.ShuffleState)
|
err = c.Client().Shuffle(ctx, !state.ShuffleState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -966,7 +1013,7 @@ func (c *Commands) Shuffle(ctx *gctx.Context) error {
|
|||||||
func (c *Commands) Repeat(ctx *gctx.Context) error {
|
func (c *Commands) Repeat(ctx *gctx.Context) error {
|
||||||
state, err := c.Client().PlayerState(ctx)
|
state, err := c.Client().PlayerState(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to get current playstate")
|
return fmt.Errorf("failed to get current playstate")
|
||||||
}
|
}
|
||||||
newState := "off"
|
newState := "off"
|
||||||
if state.RepeatState == "off" {
|
if state.RepeatState == "off" {
|
||||||
@ -985,16 +1032,12 @@ func (c *Commands) TrackList(ctx *gctx.Context, page int) (*spotify.SavedTrackPa
|
|||||||
return c.Client().CurrentUsersTracks(ctx, spotify.Limit(50), spotify.Offset((page-1)*50))
|
return c.Client().CurrentUsersTracks(ctx, spotify.Limit(50), spotify.Offset((page-1)*50))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) GetQueue(ctx *gctx.Context) (*spotify.Queue, error) {
|
|
||||||
return c.Client().GetQueue(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Commands) Playlists(ctx *gctx.Context, page int) (*spotify.SimplePlaylistPage, error) {
|
func (c *Commands) Playlists(ctx *gctx.Context, page int) (*spotify.SimplePlaylistPage, error) {
|
||||||
return c.Client().CurrentUsersPlaylists(ctx, spotify.Limit(50), spotify.Offset((page-1)*50))
|
return c.Client().CurrentUsersPlaylists(ctx, spotify.Limit(50), spotify.Offset((page-1)*50))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) PlaylistTracks(ctx *gctx.Context, playlist spotify.ID, page int) (*spotify.PlaylistTrackPage, error) {
|
func (c *Commands) PlaylistTracks(ctx *gctx.Context, playlist spotify.ID, page int) (*spotify.PlaylistItemPage, error) {
|
||||||
return c.Client().GetPlaylistTracks(ctx, playlist, spotify.Limit(50), spotify.Offset((page-1)*50))
|
return c.Client().GetPlaylistItems(ctx, playlist, spotify.Limit(50), spotify.Offset((page-1)*50))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) FormatState(state *spotify.PlayerState) (string, error) {
|
func (c *Commands) FormatState(state *spotify.PlayerState) (string, error) {
|
||||||
@ -1012,7 +1055,7 @@ func (c *Commands) PrintPlaying(current *spotify.CurrentlyPlaying) error {
|
|||||||
if !current.Playing {
|
if !current.Playing {
|
||||||
icon = "⏸"
|
icon = "⏸"
|
||||||
}
|
}
|
||||||
fmt.Println(fmt.Sprintf("%s %s - %s", icon, current.Item.Name, current.Item.Artists[0].Name))
|
fmt.Printf("%s %s - %s\n", icon, current.Item.Name, current.Item.Artists[0].Name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1031,7 +1074,7 @@ func (c *Commands) SetDevice(ctx *gctx.Context, device spotify.PlayerDevice) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
configDir, _ := os.UserConfigDir()
|
configDir, _ := os.UserConfigDir()
|
||||||
err = os.WriteFile(filepath.Join(configDir, "gospt/device.json"), out, 0o644)
|
err = os.WriteFile(filepath.Join(configDir, "gospt/device.json"), out, 0o600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1049,19 +1092,19 @@ func isNoActiveError(err error) bool {
|
|||||||
func (c *Commands) RadioFromPlaylist(ctx *gctx.Context, playlist spotify.SimplePlaylist) error {
|
func (c *Commands) RadioFromPlaylist(ctx *gctx.Context, playlist spotify.SimplePlaylist) error {
|
||||||
total := playlist.Tracks.Total
|
total := playlist.Tracks.Total
|
||||||
if total == 0 {
|
if total == 0 {
|
||||||
return fmt.Errorf("This playlist is empty")
|
return fmt.Errorf("this playlist is empty")
|
||||||
}
|
}
|
||||||
pages := int(math.Ceil(float64(total) / 50))
|
pages := int(math.Ceil(float64(total) / 50))
|
||||||
randomPage := 1
|
randomPage := 1
|
||||||
if pages > 1 {
|
if pages > 1 {
|
||||||
randomPage = frand.Intn(int(pages-1)) + 1
|
randomPage = rand.Intn(pages-1) + 1
|
||||||
}
|
}
|
||||||
playlistPage, err := c.Client().GetPlaylistItems(ctx, playlist.ID, spotify.Limit(50), spotify.Offset((randomPage-1)*50))
|
playlistPage, err := c.Client().GetPlaylistItems(ctx, playlist.ID, spotify.Limit(50), spotify.Offset((randomPage-1)*50))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pageSongs := playlistPage.Items
|
pageSongs := playlistPage.Items
|
||||||
frand.Shuffle(len(pageSongs), func(i, j int) { pageSongs[i], pageSongs[j] = pageSongs[j], pageSongs[i] })
|
rand.Shuffle(len(pageSongs), func(i, j int) { pageSongs[i], pageSongs[j] = pageSongs[j], pageSongs[i] })
|
||||||
seedCount := 5
|
seedCount := 5
|
||||||
if len(pageSongs) < seedCount {
|
if len(pageSongs) < seedCount {
|
||||||
seedCount = len(pageSongs)
|
seedCount = len(pageSongs)
|
||||||
@ -1083,19 +1126,19 @@ func (c *Commands) RadioFromAlbum(ctx *gctx.Context, album spotify.SimpleAlbum)
|
|||||||
}
|
}
|
||||||
total := tracks.Total
|
total := tracks.Total
|
||||||
if total == 0 {
|
if total == 0 {
|
||||||
return fmt.Errorf("This playlist is empty")
|
return fmt.Errorf("this playlist is empty")
|
||||||
}
|
}
|
||||||
pages := int(math.Ceil(float64(total) / 50))
|
pages := int(math.Ceil(float64(total) / 50))
|
||||||
randomPage := 1
|
randomPage := 1
|
||||||
if pages > 1 {
|
if pages > 1 {
|
||||||
randomPage = frand.Intn(int(pages-1)) + 1
|
randomPage = rand.Intn(pages-1) + 1
|
||||||
}
|
}
|
||||||
albumTrackPage, err := c.AlbumTracks(ctx, album.ID, randomPage)
|
albumTrackPage, err := c.AlbumTracks(ctx, album.ID, randomPage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pageSongs := albumTrackPage.Tracks
|
pageSongs := albumTrackPage.Tracks
|
||||||
frand.Shuffle(len(pageSongs), func(i, j int) { pageSongs[i], pageSongs[j] = pageSongs[j], pageSongs[i] })
|
rand.Shuffle(len(pageSongs), func(i, j int) { pageSongs[i], pageSongs[j] = pageSongs[j], pageSongs[i] })
|
||||||
seedCount := 5
|
seedCount := 5
|
||||||
if len(pageSongs) < seedCount {
|
if len(pageSongs) < seedCount {
|
||||||
seedCount = len(pageSongs)
|
seedCount = len(pageSongs)
|
||||||
@ -1116,19 +1159,19 @@ func (c *Commands) RadioFromSavedTracks(ctx *gctx.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if savedSongs.Total == 0 {
|
if savedSongs.Total == 0 {
|
||||||
return fmt.Errorf("You have no saved songs")
|
return fmt.Errorf("you have no saved songs")
|
||||||
}
|
}
|
||||||
pages := int(math.Ceil(float64(savedSongs.Total) / 50))
|
pages := int(math.Ceil(float64(savedSongs.Total) / 50))
|
||||||
randomPage := 1
|
randomPage := 1
|
||||||
if pages > 1 {
|
if pages > 1 {
|
||||||
randomPage = frand.Intn(int(pages-1)) + 1
|
randomPage = rand.Intn(pages-1) + 1
|
||||||
}
|
}
|
||||||
trackPage, err := c.Client().CurrentUsersTracks(ctx, spotify.Limit(50), spotify.Offset(randomPage*50))
|
trackPage, err := c.Client().CurrentUsersTracks(ctx, spotify.Limit(50), spotify.Offset(randomPage*50))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pageSongs := trackPage.Tracks
|
pageSongs := trackPage.Tracks
|
||||||
frand.Shuffle(len(pageSongs), func(i, j int) { pageSongs[i], pageSongs[j] = pageSongs[j], pageSongs[i] })
|
rand.Shuffle(len(pageSongs), func(i, j int) { pageSongs[i], pageSongs[j] = pageSongs[j], pageSongs[i] })
|
||||||
seedCount := 4
|
seedCount := 4
|
||||||
seedIds := []spotify.ID{}
|
seedIds := []spotify.ID{}
|
||||||
for idx, song := range pageSongs {
|
for idx, song := range pageSongs {
|
||||||
@ -1181,9 +1224,6 @@ func (c *Commands) RadioGivenList(ctx *gctx.Context, song_ids []spotify.ID, name
|
|||||||
}
|
}
|
||||||
err = c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
err = c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
||||||
PlaybackContext: &radioPlaylist.URI,
|
PlaybackContext: &radioPlaylist.URI,
|
||||||
PlaybackOffset: &spotify.PlaybackOffset{
|
|
||||||
Position: 0,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isNoActiveError(err) {
|
if isNoActiveError(err) {
|
||||||
@ -1193,9 +1233,6 @@ func (c *Commands) RadioGivenList(ctx *gctx.Context, song_ids []spotify.ID, name
|
|||||||
}
|
}
|
||||||
err = c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
err = c.Client().PlayOpt(ctx, &spotify.PlayOptions{
|
||||||
PlaybackContext: &radioPlaylist.URI,
|
PlaybackContext: &radioPlaylist.URI,
|
||||||
PlaybackOffset: &spotify.PlaybackOffset{
|
|
||||||
Position: 0,
|
|
||||||
},
|
|
||||||
DeviceID: &deviceId,
|
DeviceID: &deviceId,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1204,7 +1241,7 @@ func (c *Commands) RadioGivenList(ctx *gctx.Context, song_ids []spotify.ID, name
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
id := frand.Intn(len(recomendationIds)-2) + 1
|
id := rand.Intn(len(recomendationIds)-2) + 1
|
||||||
seed := spotify.Seeds{
|
seed := spotify.Seeds{
|
||||||
Tracks: []spotify.ID{recomendationIds[id]},
|
Tracks: []spotify.ID{recomendationIds[id]},
|
||||||
}
|
}
|
||||||
@ -1261,29 +1298,6 @@ func (c *Commands) activateDevice(ctx *gctx.Context) (spotify.ID, error) {
|
|||||||
return device.ID, nil
|
return device.ID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) getDefaultDevice(ctx *gctx.Context) (spotify.ID, error) {
|
|
||||||
configDir, _ := os.UserConfigDir()
|
|
||||||
if _, err := os.Stat(filepath.Join(configDir, "gospt/device.json")); err == nil {
|
|
||||||
deviceFile, err := os.Open(filepath.Join(configDir, "gospt/device.json"))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer deviceFile.Close()
|
|
||||||
deviceValue, err := io.ReadAll(deviceFile)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
var device *spotify.PlayerDevice
|
|
||||||
err = json.Unmarshal(deviceValue, &device)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return device.ID, nil
|
|
||||||
} else {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Commands) GetRadioPlaylist(ctx *gctx.Context, name string) (*spotify.FullPlaylist, *sql.DB, error) {
|
func (c *Commands) GetRadioPlaylist(ctx *gctx.Context, name string) (*spotify.FullPlaylist, *sql.DB, error) {
|
||||||
configDir, _ := os.UserConfigDir()
|
configDir, _ := os.UserConfigDir()
|
||||||
playlistFile, err := os.ReadFile(filepath.Join(configDir, "gospt/radio.json"))
|
playlistFile, err := os.ReadFile(filepath.Join(configDir, "gospt/radio.json"))
|
||||||
@ -1299,13 +1313,17 @@ func (c *Commands) GetRadioPlaylist(ctx *gctx.Context, name string) (*spotify.Fu
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
db, err := sql.Open("sqlite", filepath.Join(configDir, "gospt/radio.db"))
|
db, err := sql.Open("sqlite", filepath.Join(configDir, "gospt/radio.db"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
return playlist, db, nil
|
return playlist, db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) CreateRadioPlaylist(ctx *gctx.Context, name string) (*spotify.FullPlaylist, *sql.DB, error) {
|
func (c *Commands) CreateRadioPlaylist(ctx *gctx.Context, name string) (*spotify.FullPlaylist, *sql.DB, error) {
|
||||||
// private flag doesnt work
|
// private flag doesnt work
|
||||||
configDir, _ := os.UserConfigDir()
|
configDir, _ := os.UserConfigDir()
|
||||||
playlist, err := c.Client().CreatePlaylistForUser(ctx, c.User(), name+" - autoradio", "Automanaged radio playlist", false, false)
|
playlist, err := c.Client().
|
||||||
|
CreatePlaylistForUser(ctx, c.User(), name+" - autoradio", "Automanaged radio playlist", false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@ -1313,11 +1331,14 @@ func (c *Commands) CreateRadioPlaylist(ctx *gctx.Context, name string) (*spotify
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
err = os.WriteFile(filepath.Join(configDir, "gospt/radio.json"), raw, 0o644)
|
err = os.WriteFile(filepath.Join(configDir, "gospt/radio.json"), raw, 0o600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
db, err := sql.Open("sqlite", filepath.Join(configDir, "gospt/radio.db"))
|
db, err := sql.Open("sqlite", filepath.Join(configDir, "gospt/radio.db"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
db.QueryContext(ctx, "DROP TABLE IF EXISTS radio")
|
db.QueryContext(ctx, "DROP TABLE IF EXISTS radio")
|
||||||
db.QueryContext(ctx, "CREATE TABLE IF NOT EXISTS radio (id string PRIMARY KEY)")
|
db.QueryContext(ctx, "CREATE TABLE IF NOT EXISTS radio (id string PRIMARY KEY)")
|
||||||
return playlist, db, nil
|
return playlist, db, nil
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
package tui
|
package tui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gitea.asdf.cafe/abs3nt/gospt/src/commands"
|
|
||||||
"gitea.asdf.cafe/abs3nt/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, commands *commands.Commands, 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, uri, pos)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -87,15 +86,35 @@ func HandlePlayTrack(ctx *gctx.Context, commands *commands.Commands, track spoti
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = commands.Next(ctx, 1)
|
err = commands.Next(ctx, 1, false)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleNextInQueue(ctx *gctx.Context, commands *commands.Commands, amt int) {
|
||||||
|
err := commands.Next(ctx, amt, true)
|
||||||
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleSetDevice(ctx *gctx.Context, commands *commands.Commands, player spotify.PlayerDevice) {
|
func HandleSetDevice(ctx *gctx.Context, commands *commands.Commands, player spotify.PlayerDevice) {
|
||||||
var err error
|
err := commands.SetDevice(ctx, player)
|
||||||
err = commands.SetDevice(ctx, player)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
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 := m.commands.ArtistAlbums(m.ctx, 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
|
||||||
}
|
}
|
||||||
@ -31,7 +35,6 @@ func (m *mainModel) LoadMoreItems() {
|
|||||||
return
|
return
|
||||||
case "artists":
|
case "artists":
|
||||||
artists, err := m.commands.UserArtists(m.ctx, (page + 1))
|
artists, err := m.commands.UserArtists(m.ctx, (page + 1))
|
||||||
page++
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -51,7 +54,6 @@ func (m *mainModel) LoadMoreItems() {
|
|||||||
return
|
return
|
||||||
case "album":
|
case "album":
|
||||||
tracks, err := m.commands.AlbumTracks(m.ctx, 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
|
||||||
}
|
}
|
||||||
@ -72,7 +74,6 @@ func (m *mainModel) LoadMoreItems() {
|
|||||||
return
|
return
|
||||||
case "albums":
|
case "albums":
|
||||||
albums, err := m.commands.UserAlbums(m.ctx, (page + 1))
|
albums, err := m.commands.UserAlbums(m.ctx, (page + 1))
|
||||||
page++
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -92,7 +93,6 @@ func (m *mainModel) LoadMoreItems() {
|
|||||||
return
|
return
|
||||||
case "main":
|
case "main":
|
||||||
playlists, err := m.commands.Playlists(m.ctx, (page + 1))
|
playlists, err := m.commands.Playlists(m.ctx, (page + 1))
|
||||||
page++
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -110,19 +110,18 @@ func (m *mainModel) LoadMoreItems() {
|
|||||||
main_updates <- m
|
main_updates <- m
|
||||||
return
|
return
|
||||||
case "playlist":
|
case "playlist":
|
||||||
tracks, err := m.commands.PlaylistTracks(m.ctx, 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 {
|
||||||
@ -132,11 +131,9 @@ func (m *mainModel) LoadMoreItems() {
|
|||||||
return
|
return
|
||||||
case "tracks":
|
case "tracks":
|
||||||
tracks, err := m.commands.TrackList(m.ctx, (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{
|
||||||
|
287
src/tui/main.go
287
src/tui/main.go
@ -5,9 +5,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.asdf.cafe/abs3nt/gospt/src/commands"
|
|
||||||
"gitea.asdf.cafe/abs3nt/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"
|
||||||
@ -16,6 +13,9 @@ 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 (
|
||||||
@ -25,30 +25,33 @@ var (
|
|||||||
playbackContext string
|
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 {
|
||||||
@ -88,50 +91,55 @@ type mainModel struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *mainModel) PlayRadio() {
|
func (m *mainModel) PlayRadio() {
|
||||||
m.list.NewStatusMessage("Starting radio for " + m.list.SelectedItem().(mainItem).Title())
|
go m.SendMessage("Starting radio for "+m.list.SelectedItem().(mainItem).Title(), 2*time.Second)
|
||||||
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.commands, selectedItem.(spotify.SimplePlaylist))
|
go HandlePlaylistRadio(m.ctx, m.commands, item)
|
||||||
return
|
return
|
||||||
case *spotify.SavedTrackPage:
|
case *spotify.SavedTrackPage:
|
||||||
go HandleLibraryRadio(m.ctx, m.commands)
|
go HandleLibraryRadio(m.ctx, m.commands)
|
||||||
return
|
return
|
||||||
case spotify.SimpleAlbum:
|
case spotify.SimpleAlbum:
|
||||||
go HandleAlbumRadio(m.ctx, m.commands, selectedItem.(spotify.SimpleAlbum))
|
go HandleAlbumRadio(m.ctx, m.commands, item)
|
||||||
return
|
return
|
||||||
case spotify.FullAlbum:
|
case spotify.FullAlbum:
|
||||||
go HandleAlbumRadio(m.ctx, m.commands, selectedItem.(spotify.FullAlbum).SimpleAlbum)
|
go HandleAlbumRadio(m.ctx, m.commands, item.SimpleAlbum)
|
||||||
return
|
return
|
||||||
case spotify.SimpleArtist:
|
case spotify.SimpleArtist:
|
||||||
go HandleArtistRadio(m.ctx, m.commands, selectedItem.(spotify.SimpleArtist))
|
go HandleArtistRadio(m.ctx, m.commands, item)
|
||||||
return
|
return
|
||||||
case spotify.FullArtist:
|
case spotify.FullArtist:
|
||||||
go HandleArtistRadio(m.ctx, m.commands, selectedItem.(spotify.FullArtist).SimpleArtist)
|
go HandleArtistRadio(m.ctx, m.commands, item.SimpleArtist)
|
||||||
return
|
return
|
||||||
case spotify.SimpleTrack:
|
case spotify.SimpleTrack:
|
||||||
go HandleRadio(m.ctx, m.commands, selectedItem.(spotify.SimpleTrack))
|
go HandleRadio(m.ctx, m.commands, item)
|
||||||
return
|
return
|
||||||
case spotify.FullTrack:
|
case spotify.FullTrack:
|
||||||
go HandleRadio(m.ctx, m.commands, selectedItem.(spotify.FullTrack).SimpleTrack)
|
go HandleRadio(m.ctx, m.commands, item.SimpleTrack)
|
||||||
return
|
return
|
||||||
case spotify.PlaylistTrack:
|
case spotify.PlaylistTrack:
|
||||||
go HandleRadio(m.ctx, m.commands, selectedItem.(spotify.PlaylistTrack).Track.SimpleTrack)
|
go HandleRadio(m.ctx, m.commands, item.Track.SimpleTrack)
|
||||||
|
return
|
||||||
|
case spotify.PlaylistItem:
|
||||||
|
go HandleRadio(m.ctx, m.commands, item.Track.Track.SimpleTrack)
|
||||||
return
|
return
|
||||||
case spotify.SavedTrack:
|
case spotify.SavedTrack:
|
||||||
go HandleRadio(m.ctx, m.commands, selectedItem.(spotify.SavedTrack).SimpleTrack)
|
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
|
||||||
new_items, err := MainView(m.ctx, m.commands)
|
new_items, err := MainView(m.ctx, m.commands)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
m.list.SetItems(new_items)
|
m.list.SetItems(new_items)
|
||||||
case Album:
|
case Album:
|
||||||
@ -203,48 +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
|
||||||
switch item.(type) {
|
switch converted := 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)
|
||||||
m.list.NewStatusMessage("Copying link to " + m.list.SelectedItem().(mainItem).Title())
|
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)
|
||||||
m.list.NewStatusMessage("Copying link to " + m.list.SelectedItem().(mainItem).Title())
|
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)
|
||||||
m.list.NewStatusMessage("Copying link to " + m.list.SelectedItem().(mainItem).Title())
|
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)
|
||||||
m.list.NewStatusMessage("Copying link to " + m.list.SelectedItem().(mainItem).Title())
|
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)
|
||||||
m.list.NewStatusMessage("Copying link to " + m.list.SelectedItem().(mainItem).Title())
|
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)
|
||||||
m.list.NewStatusMessage("Copying link to " + m.list.SelectedItem().(mainItem).Title())
|
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)
|
||||||
m.list.NewStatusMessage("Copying link to " + m.list.SelectedItem().(mainItem).Title())
|
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)
|
||||||
m.list.NewStatusMessage("Copying link to " + m.list.SelectedItem().(mainItem).Title())
|
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)
|
||||||
m.list.NewStatusMessage("Copying link to " + m.list.SelectedItem().(mainItem).Title())
|
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)
|
||||||
m.list.NewStatusMessage("Copying link to " + m.list.SelectedItem().(mainItem).Title())
|
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
|
||||||
new_items, err := SearchArtistsView(m.ctx, m.commands, m.list.SelectedItem().(mainItem).SpotifyItem.(*spotify.FullArtistPage))
|
new_items, err := SearchArtistsView(m.ctx, m.commands, item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -252,7 +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
|
||||||
new_items, err := SearchAlbumsView(m.ctx, m.commands, m.list.SelectedItem().(mainItem).SpotifyItem.(*spotify.SimpleAlbumPage))
|
new_items, err := SearchAlbumsView(m.ctx, m.commands, item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -260,8 +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)
|
||||||
new_items, err := SearchPlaylistsView(m.ctx, m.commands, playlists)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -269,7 +350,7 @@ func (m *mainModel) SelectItem() error {
|
|||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
case *spotify.FullTrackPage:
|
case *spotify.FullTrackPage:
|
||||||
m.mode = SearchTracks
|
m.mode = SearchTracks
|
||||||
new_items, err := SearchTracksView(m.ctx, m.commands, m.list.SelectedItem().(mainItem).SpotifyItem.(*spotify.FullTrackPage))
|
new_items, err := SearchTracksView(m.ctx, m.commands, item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -277,6 +358,7 @@ func (m *mainModel) SelectItem() error {
|
|||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
}
|
}
|
||||||
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)
|
||||||
new_items, err := ArtistAlbumsView(m.ctx, m.artist.ID, m.commands)
|
new_items, err := ArtistAlbumsView(m.ctx, m.artist.ID, m.commands)
|
||||||
@ -286,6 +368,7 @@ func (m *mainModel) SelectItem() error {
|
|||||||
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)
|
||||||
new_items, err := AlbumTracksView(m.ctx, m.album.ID, m.commands)
|
new_items, err := AlbumTracksView(m.ctx, m.album.ID, m.commands)
|
||||||
@ -295,6 +378,7 @@ func (m *mainModel) SelectItem() error {
|
|||||||
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)
|
||||||
new_items, err := AlbumTracksView(m.ctx, m.album.ID, m.commands)
|
new_items, err := AlbumTracksView(m.ctx, m.album.ID, m.commands)
|
||||||
@ -304,6 +388,7 @@ func (m *mainModel) SelectItem() error {
|
|||||||
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
|
||||||
@ -314,7 +399,16 @@ func (m *mainModel) SelectItem() error {
|
|||||||
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
|
||||||
new_items, err := ArtistsView(m.ctx, m.commands)
|
new_items, err := ArtistsView(m.ctx, m.commands)
|
||||||
@ -333,9 +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)
|
||||||
new_items, err := PlaylistView(m.ctx, m.commands, playlist)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -351,6 +444,7 @@ func (m *mainModel) SelectItem() error {
|
|||||||
m.list.ResetSelected()
|
m.list.ResetSelected()
|
||||||
}
|
}
|
||||||
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)
|
||||||
new_items, err := AlbumTracksView(m.ctx, m.album.ID, m.commands)
|
new_items, err := AlbumTracksView(m.ctx, m.album.ID, m.commands)
|
||||||
@ -378,18 +472,19 @@ func (m *mainModel) SelectItem() error {
|
|||||||
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:
|
||||||
go HandlePlayWithContext(m.ctx, m.commands, &m.album.URI, m.list.Cursor()+(m.list.Paginator.Page*m.list.Paginator.TotalPages))
|
pos := m.list.Cursor() + (m.list.Paginator.Page * m.list.Paginator.TotalPages)
|
||||||
|
go HandlePlayWithContext(m.ctx, m.commands, &m.album.URI, &pos)
|
||||||
case Playlist, SearchPlaylist:
|
case Playlist, SearchPlaylist:
|
||||||
go HandlePlayWithContext(m.ctx, m.commands, &m.playlist.URI, m.list.Cursor()+(m.list.Paginator.Page*m.list.Paginator.PerPage))
|
pos := m.list.Cursor() + (m.list.Paginator.Page * m.list.Paginator.PerPage)
|
||||||
|
go HandlePlayWithContext(m.ctx, m.commands, &m.playlist.URI, &pos)
|
||||||
case Tracks:
|
case Tracks:
|
||||||
go HandlePlayLikedSong(m.ctx, m.commands, m.list.Cursor()+(m.list.Paginator.Page*m.list.Paginator.PerPage))
|
go HandlePlayLikedSong(m.ctx, m.commands, m.list.Cursor()+(m.list.Paginator.Page*m.list.Paginator.PerPage))
|
||||||
case SearchTracks:
|
case SearchTracks:
|
||||||
go HandlePlayTrack(m.ctx, m.commands, m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.FullTrack).ID)
|
go HandlePlayTrack(m.ctx, m.commands, m.list.SelectedItem().(mainItem).SpotifyItem.(spotify.FullTrack).ID)
|
||||||
case Devices:
|
case Devices:
|
||||||
go HandleSetDevice(m.ctx, m.commands, 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.commands)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -399,7 +494,7 @@ func (m *mainModel) SelectItem() error {
|
|||||||
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()
|
||||||
}
|
}
|
||||||
@ -437,7 +532,7 @@ func (m *mainModel) TickPlayback() {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m mainModel) View() string {
|
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())
|
||||||
}
|
}
|
||||||
@ -497,7 +592,7 @@ func (m *mainModel) getContext(playing *spotify.CurrentlyPlaying) (string, error
|
|||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
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:
|
||||||
@ -505,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()
|
||||||
@ -519,6 +614,17 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
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
|
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()
|
||||||
@ -526,6 +632,7 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
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)
|
||||||
|
if !showingMessage {
|
||||||
m.list.NewStatusMessage(
|
m.list.NewStatusMessage(
|
||||||
fmt.Sprintf("Now playing %s by %s - %s %s/%s : %s",
|
fmt.Sprintf("Now playing %s by %s - %s %s/%s : %s",
|
||||||
m.playing.Item.Name,
|
m.playing.Item.Name,
|
||||||
@ -535,6 +642,7 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
(time.Duration(m.playing.Item.Duration) * time.Millisecond).Round(time.Second),
|
(time.Duration(m.playing.Item.Duration) * time.Millisecond).Round(time.Second),
|
||||||
m.playbackContext),
|
m.playbackContext),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
return m, cmd
|
return m, cmd
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
// quit
|
// quit
|
||||||
@ -580,7 +688,6 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
}
|
}
|
||||||
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" {
|
||||||
@ -591,9 +698,20 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
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
|
||||||
@ -647,7 +765,7 @@ func InitMain(ctx *gctx.Context, c *commands.Commands, mode Mode) (tea.Model, er
|
|||||||
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,
|
||||||
commands: c,
|
commands: c,
|
||||||
@ -658,27 +776,30 @@ func InitMain(ctx *gctx.Context, c *commands.Commands, mode Mode) (tea.Model, er
|
|||||||
go m.TickPlayback()
|
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,10 +1,8 @@
|
|||||||
package tui
|
package tui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"git.asdf.cafe/abs3nt/gospt/src/commands"
|
||||||
|
"git.asdf.cafe/abs3nt/gospt/src/gctx"
|
||||||
"gitea.asdf.cafe/abs3nt/gospt/src/commands"
|
|
||||||
"gitea.asdf.cafe/abs3nt/gospt/src/gctx"
|
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
)
|
)
|
||||||
@ -13,10 +11,10 @@ import (
|
|||||||
func StartTea(ctx *gctx.Context, cmd *commands.Commands, mode string) error {
|
func StartTea(ctx *gctx.Context, cmd *commands.Commands, mode string) error {
|
||||||
m, err := InitMain(ctx, cmd, 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
|
||||||
|
151
src/tui/views.go
151
src/tui/views.go
@ -2,16 +2,19 @@ package tui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.asdf.cafe/abs3nt/gospt/src/commands"
|
"git.asdf.cafe/abs3nt/gospt/src/commands"
|
||||||
"gitea.asdf.cafe/abs3nt/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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const regex = `<.*?>`
|
||||||
|
|
||||||
func DeviceView(ctx *gctx.Context, commands *commands.Commands) ([]list.Item, error) {
|
func DeviceView(ctx *gctx.Context, commands *commands.Commands) ([]list.Item, error) {
|
||||||
items := []list.Item{}
|
items := []list.Item{}
|
||||||
devices, err := commands.Client().PlayerDevices(ctx)
|
devices, err := commands.Client().PlayerDevices(ctx)
|
||||||
@ -28,25 +31,54 @@ func DeviceView(ctx *gctx.Context, commands *commands.Commands) ([]list.Item, er
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func PlaylistView(ctx *gctx.Context, commands *commands.Commands, 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, 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 PlaylistView(ctx *gctx.Context, commands *commands.Commands, playlist spotify.SimplePlaylist) ([]list.Item, error) {
|
||||||
|
items := []list.Item{}
|
||||||
|
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) {
|
func ArtistsView(ctx *gctx.Context, commands *commands.Commands) ([]list.Item, error) {
|
||||||
items := []list.Item{}
|
items := []list.Item{}
|
||||||
artists, err := commands.UserArtists(ctx, 1)
|
artists, err := commands.UserArtists(ctx, 1)
|
||||||
@ -57,7 +89,7 @@ func ArtistsView(ctx *gctx.Context, commands *commands.Commands) ([]list.Item, e
|
|||||||
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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -70,7 +102,7 @@ func SearchArtistsView(ctx *gctx.Context, commands *commands.Commands, 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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -84,27 +116,13 @@ func SearchView(ctx *gctx.Context, commands *commands.Commands, search string) (
|
|||||||
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,
|
||||||
@ -124,7 +142,7 @@ func AlbumsView(ctx *gctx.Context, commands *commands.Commands) ([]list.Item, er
|
|||||||
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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -136,7 +154,7 @@ func SearchPlaylistsView(ctx *gctx.Context, commands *commands.Commands, playlis
|
|||||||
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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -149,7 +167,7 @@ func SearchAlbumsView(ctx *gctx.Context, commands *commands.Commands, albums *sp
|
|||||||
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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -227,57 +245,36 @@ func SavedTracksView(ctx *gctx.Context, commands *commands.Commands) ([]list.Ite
|
|||||||
}
|
}
|
||||||
|
|
||||||
func MainView(ctx *gctx.Context, commands *commands.Commands) ([]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() {
|
|
||||||
defer wg.Done()
|
|
||||||
var err error
|
|
||||||
saved_items, err = commands.TrackList(ctx, 1)
|
saved_items, err = commands.TrackList(ctx, 1)
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
})
|
||||||
}()
|
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Go(func() (err error) {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
var err error
|
|
||||||
playlists, err = commands.Playlists(ctx, 1)
|
playlists, err = commands.Playlists(ctx, 1)
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
})
|
||||||
}()
|
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Go(func() (err error) {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
var err error
|
|
||||||
artists, err = commands.UserArtists(ctx, 1)
|
artists, err = commands.UserArtists(ctx, 1)
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
})
|
||||||
}()
|
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Go(func() (err error) {
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
var err error
|
|
||||||
albums, err = commands.UserAlbums(ctx, 1)
|
albums, err = commands.UserAlbums(ctx, 1)
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
return
|
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, commands *commands.Commands) ([]list.Item, erro
|
|||||||
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