This commit is contained in:
abs3nt 2024-02-24 19:11:14 -08:00
parent f208f0f92f
commit 5dd659818e
Signed by: abs3nt
GPG Key ID: A7BD96A8BAB04C09
8 changed files with 248 additions and 203 deletions

View File

@ -1,20 +1,21 @@
build: wallhaven_dl build: wallhaven_dl
wallhaven_dl: $(shell find . -name '*.go') wallhaven_dl: $(shell find . -name '*.go')
go build -o wallhaven_dl . go build -o dist/ .
run: run: build
go run main.go ./dist/wallhaven_dl
tidy: tidy:
go mod tidy go mod tidy
clean: clean:
rm -f wallhaven_dl rm -rf dist
uninstall: uninstall:
rm -f /usr/bin/wallhaven_dl rm -f /usr/bin/wallhaven_dl
rm -f /usr/share/zsh/site-functions/_wallhaven_dl
install: install:
cp wallhaven_dl /usr/bin cp ./dist/wallhaven_dl /usr/bin
./wallhaven_dl completion zsh > /usr/share/zsh/site-functions/_wallhaven_dl cp ./completions/_wallhaven_dl /usr/share/zsh/site-functions/_wallhaven_dl

View File

@ -1,21 +0,0 @@
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "wallhaven_dl",
Short: "A wallpaper downloader and setter",
}
// Execute executes the root command.
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

View File

@ -1,170 +0,0 @@
package cmd
import (
"fmt"
"math/rand"
"os"
"os/exec"
"path"
"time"
"github.com/spf13/cobra"
"git.asdf.cafe/abs3nt/wallhaven_dl/src/wallhaven"
)
func init() {
rootCmd.AddCommand(searchCmd)
searchCmd.PersistentFlags().StringVarP(
&searchRange,
"range",
"r",
"1y",
"range for search.",
)
searchCmd.PersistentFlags().StringVarP(
&searchPurity,
"purity",
"p",
"110",
"purity for the search.",
)
searchCmd.PersistentFlags().StringVarP(
&searchCategories,
"categories",
"c",
"010",
"categories for the search.",
)
searchCmd.PersistentFlags().StringVarP(
&searchSorting,
"sort",
"s",
"toplist",
"sort by for results, valid sorts: date_added, relevance, random, views, favorites, searchlist.",
)
searchCmd.PersistentFlags().StringVarP(
&searchOrder,
"order",
"o",
"desc",
"sort order for results, valid sorts: asc desc.",
)
searchCmd.PersistentFlags().IntVarP(
&searchPage,
"maxPage",
"m",
5,
"number of pages to randomly choose wallpaper from.",
)
searchCmd.PersistentFlags().StringSliceVar(
&searchRatios,
"ratios",
[]string{"16x9", "16x10"},
"ratios to search for.",
)
searchCmd.PersistentFlags().StringVar(
&searchAtLeast,
"at-least",
"2560x1440",
"minimum resolution for results.",
)
searchCmd.PersistentFlags().StringVarP(
&searchScript,
"script",
"t",
"",
"script to run after downloading the wallpaper",
)
searchCmd.PersistentFlags().StringVarP(
&downloadPath,
"download-path",
"d",
"",
"directory to download the image too",
)
}
var (
searchRange string
searchPurity string
searchCategories string
searchSorting string
searchOrder string
searchAtLeast string
searchScript string
downloadPath string
searchRatios []string
searchPage int
searchCmd = &cobra.Command{
Use: "search",
Aliases: []string{"s"},
Args: cobra.RangeArgs(0, 1),
Short: "Wallhaven downloader with the option to run a script after the image has been downloaded",
RunE: func(cmd *cobra.Command, args []string) error {
return search(args)
},
}
)
func search(args []string) error {
seed := rand.NewSource(time.Now().UnixNano())
r := rand.New(seed)
s := &wallhaven.Search{
Categories: searchCategories,
Purities: searchPurity,
Sorting: searchSorting,
Order: searchOrder,
TopRange: searchRange,
AtLeast: searchAtLeast,
Ratios: searchRatios,
Page: r.Intn(searchPage) + 1,
}
if len(args) > 0 {
s.Query = wallhaven.Q{
Tags: []string{args[0]},
}
}
results, err := wallhaven.SearchWallpapers(s)
if err != nil {
return err
}
resultPath, err := getOrDownload(results, r)
if err != nil {
return err
}
if searchScript != "" {
err = runScript(resultPath, searchScript)
if err != nil {
return err
}
}
return nil
}
func getOrDownload(results *wallhaven.SearchResults, r *rand.Rand) (string, error) {
if len(results.Data) == 0 {
return "", fmt.Errorf("no wallpapers found")
}
homedir, _ := os.UserHomeDir()
if downloadPath == "" {
downloadPath = path.Join(homedir, "Pictures/Wallpapers")
}
result := results.Data[r.Intn(len(results.Data))]
fullPath := path.Join(downloadPath, path.Base(result.Path))
if _, err := os.Stat(fullPath); err != nil {
err = result.Download(path.Join(downloadPath))
if err != nil {
return "", err
}
}
return fullPath, nil
}
func runScript(imgPath, script string) error {
_, err := exec.Command(script, imgPath).Output()
if err != nil {
return err
}
return nil
}

20
completions/_wallhaven_dl Normal file
View File

@ -0,0 +1,20 @@
#compdef wallhaven_dl
_cli_zsh_autocomplete() {
local -a opts
local cur
cur=${words[-1]}
if [[ "$cur" == "-"* ]]; then
opts=("${(@f)$(${words[@]:0:#words[@]-1} ${cur} --generate-shell-completion)}")
else
opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-shell-completion)}")
fi
if [[ "${opts[1]}" != "" ]]; then
_describe 'values' opts
else
_files
fi
}
compdef _cli_zsh_autocomplete wallhaven_dl

8
go.mod
View File

@ -1,10 +1,14 @@
module git.asdf.cafe/abs3nt/wallhaven_dl module git.asdf.cafe/abs3nt/wallhaven_dl
go 1.21.3 go 1.22.0
require github.com/spf13/cobra v1.7.0 require (
github.com/spf13/cobra v1.7.0
github.com/urfave/cli/v3 v3.0.0-alpha9
)
require ( require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
) )

11
go.sum
View File

@ -1,10 +1,21 @@
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
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/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/urfave/cli/v3 v3.0.0-alpha9 h1:P0RMy5fQm1AslQS+XCmy9UknDXctOmG/q/FZkUFnJSo=
github.com/urfave/cli/v3 v3.0.0-alpha9/go.mod h1:0kK/RUFHyh+yIKSfWxwheGndfnrvYSmYFVeKCh03ZUc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

204
main.go
View File

@ -1,7 +1,207 @@
package main package main
import "git.asdf.cafe/abs3nt/wallhaven_dl/cmd" import (
"context"
"fmt"
"log"
"math/rand"
"os"
"os/exec"
"path"
"time"
"github.com/urfave/cli/v3"
"git.asdf.cafe/abs3nt/wallhaven_dl/src/wallhaven"
)
func main() { func main() {
cmd.Execute() app := cli.Command{
EnableShellCompletion: true,
Name: "wallhaven_dl",
Usage: "Download wallpapers from wallhaven.cc",
Action: func(ctx context.Context, c *cli.Command) error {
fmt.Println("cli")
return nil
},
Commands: []*cli.Command{
{
Name: "search",
Usage: "Search for wallpapers",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "range",
Aliases: []string{"r"},
Value: "1y",
Validator: func(s string) error {
if s != "1d" && s != "3d" && s != "1w" && s != "1M" && s != "3M" && s != "6M" && s != "1y" {
return fmt.Errorf("range must be '1d', '3d', '1w', '1M', '3M', '6M' or '1y'")
}
return nil
},
Usage: "Time range for top sorting",
},
&cli.StringFlag{
Name: "purity",
Aliases: []string{"p"},
Value: "110",
Validator: func(s string) error {
if len(s) != 3 {
return fmt.Errorf("purity must be 3 characters long")
}
for _, c := range s {
if c != '0' && c != '1' {
return fmt.Errorf("purity must be 0 or 1")
}
}
return nil
},
Usage: "Purity of the wallpapers",
},
&cli.StringFlag{
Name: "categories",
Aliases: []string{"c"},
Value: "010",
Validator: func(s string) error {
if len(s) != 3 {
return fmt.Errorf("categories must be 3 characters long")
}
for _, c := range s {
if c != '0' && c != '1' {
return fmt.Errorf("categories must be 0 or 1")
}
}
return nil
},
Usage: "Categories of the wallpapers",
},
&cli.StringFlag{
Name: "sort",
Aliases: []string{"s"},
Value: "toplist",
Validator: func(s string) error {
if s != "relevance" && s != "random" && s != "date_added" && s != "views" && s != "favorites" &&
s != "toplist" {
return fmt.Errorf(
"sort must be 'relevance', 'random', 'date_added', 'views', 'favorites' or 'toplist'",
)
}
return nil
},
Usage: "Sorting of the wallpapers",
},
&cli.StringFlag{
Name: "order",
Aliases: []string{"o"},
Value: "desc",
Validator: func(s string) error {
if s != "asc" && s != "desc" {
return fmt.Errorf("order must be 'asc' or 'desc'")
}
return nil
},
Usage: "Order of the wallpapers",
},
&cli.IntFlag{
Name: "page",
Aliases: []string{"pg"},
Value: 5,
Usage: "Pages to search",
},
&cli.StringSliceFlag{
Name: "ratios",
Aliases: []string{"rt"},
Value: []string{"16x9", "16x10"},
Usage: "Ratios of the wallpapers",
},
&cli.StringFlag{
Name: "atLeast",
Aliases: []string{"al"},
Value: "2560x1440",
Usage: "Minimum resolution",
},
&cli.StringFlag{
Name: "scriptPath",
Aliases: []string{"sp"},
Value: "",
TakesFile: true,
Usage: "Path to the script to run after downloading",
},
&cli.StringFlag{
Name: "downloadPath",
Aliases: []string{"dp"},
Value: os.Getenv("HOME") + "/Pictures/Wallpapers",
TakesFile: true,
Usage: "Path to download the wallpapers",
},
},
Action: func(ctx context.Context, c *cli.Command) error {
return search(c)
},
},
},
}
if err := app.Run(context.Background(), os.Args); err != nil {
log.Fatal(err)
}
}
func search(c *cli.Command) error {
seed := rand.NewSource(time.Now().UnixNano())
r := rand.New(seed)
s := &wallhaven.Search{
Categories: c.String("categories"),
Purities: c.String("purity"),
Sorting: c.String("sort"),
Order: c.String("order"),
TopRange: c.String("range"),
AtLeast: c.String("atLeast"),
Ratios: c.StringSlice("ratios"),
Page: c.Int("page"),
}
query := c.Args().First()
if query != "" {
s.Query = wallhaven.Q{
Tags: []string{query},
}
}
results, err := wallhaven.SearchWallpapers(s)
if err != nil {
return err
}
resultPath, err := getOrDownload(results, r, c.String("downloadPath"))
if err != nil {
return err
}
searchScript := c.String("scriptPath")
if searchScript != "" {
err = runScript(resultPath, searchScript)
if err != nil {
return err
}
}
return nil
}
func getOrDownload(results *wallhaven.SearchResults, r *rand.Rand, downloadPath string) (string, error) {
if len(results.Data) == 0 {
return "", fmt.Errorf("no wallpapers found")
}
result := results.Data[r.Intn(len(results.Data))]
fullPath := path.Join(downloadPath, path.Base(result.Path))
if _, err := os.Stat(fullPath); err != nil {
err = result.Download(path.Join(downloadPath))
if err != nil {
return "", err
}
}
return fullPath, nil
}
func runScript(imgPath, script string) error {
_, err := exec.Command(script, imgPath).Output()
if err != nil {
return err
}
return nil
} }

View File

@ -63,7 +63,7 @@ type Search struct {
Resolutions []string Resolutions []string
Ratios []string Ratios []string
Colors []string // Colors is an array of hex colors represented as strings in #RRGGBB format Colors []string // Colors is an array of hex colors represented as strings in #RRGGBB format
Page int Page int64
} }
func (s Search) toQuery() url.Values { func (s Search) toQuery() url.Values {
@ -96,7 +96,7 @@ func (s Search) toQuery() url.Values {
v.Add("colors", strings.Join([]string(s.Colors), ",")) v.Add("colors", strings.Join([]string(s.Colors), ","))
} }
if s.Page > 0 { if s.Page > 0 {
v.Add("page", strconv.Itoa(s.Page)) v.Add("page", strconv.FormatInt(s.Page, 10))
} }
return v return v
} }