From 7fdd61b54c2f24d2866003c0eca20d71f186f284 Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Thu, 15 Sep 2022 23:44:32 +0100 Subject: [PATCH] refactor: rename from mj2n to mje --- .gitignore | 2 +- Makefile | 215 +++++++++++++++++++++++++++++ commands/midjourney.go | 13 +- commands/midjourney_recent_jobs.go | 34 +++-- commands/midjourney_words.go | 70 ++++++++++ commands/{mj2n.go => mje.go} | 72 +++++++--- commands/render.go | 2 +- go.mod | 5 +- go.sum | 9 +- main.go | 14 +- midjourney/client.go | 144 ------------------- midjourney/job.go | 57 -------- midjourney/midjourney.go | 4 - midjourney/recent_jobs.go | 124 ----------------- 14 files changed, 389 insertions(+), 376 deletions(-) create mode 100644 Makefile create mode 100644 commands/midjourney_words.go rename commands/{mj2n.go => mje.go} (57%) delete mode 100644 midjourney/client.go delete mode 100644 midjourney/job.go delete mode 100644 midjourney/midjourney.go delete mode 100644 midjourney/recent_jobs.go diff --git a/.gitignore b/.gitignore index fa21c28..144711e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .env* bin/* -mj2n +mje diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5607744 --- /dev/null +++ b/Makefile @@ -0,0 +1,215 @@ +NAME = mje +BINARY = bin/${NAME} +GOMODNAME := $(shell grep 'module' go.mod | sed -e 's/^module //') +SOURCES := $(shell find . -name "*.go" -or -name "go.mod" -or -name "go.sum" \ + -or -name "Makefile") + +# Verbose output +ifdef VERBOSE +V = -v +endif + +# +# Environment +# + +BINDIR := bin +TOOLDIR := $(BINDIR)/tools + +# Global environment variables for all targets +SHELL ?= /bin/bash +SHELL := env \ + GO111MODULE=on \ + GOBIN=$(CURDIR)/$(TOOLDIR) \ + CGO_ENABLED=1 \ + PATH='$(CURDIR)/$(BINDIR):$(CURDIR)/$(TOOLDIR):$(PATH)' \ + $(SHELL) + +# +# Defaults +# + +# Default target +.DEFAULT_GOAL := test + +# +# Tools +# + +# external tool +define tool # 1: binary-name, 2: go-import-path +TOOLS += $(TOOLDIR)/$(1) + +$(TOOLDIR)/$(1): Makefile + GOBIN="$(CURDIR)/$(TOOLDIR)" go install "$(2)" +endef + +$(eval $(call tool,godoc,golang.org/x/tools/cmd/godoc@latest)) +$(eval $(call tool,gofumpt,mvdan.cc/gofumpt@latest)) +$(eval $(call tool,goimports,golang.org/x/tools/cmd/goimports@latest)) +$(eval $(call tool,golangci-lint,github.com/golangci/golangci-lint/cmd/golangci-lint@v1.49)) +$(eval $(call tool,gomod,github.com/Helcaraxan/gomod@latest)) +$(eval $(call tool,mockgen,github.com/golang/mock/mockgen@v1.6.0)) + +.PHONY: tools +tools: $(TOOLS) + +# +# Build +# + +VERSION ?= $(shell git describe --tags --exact 2>/dev/null) +ifeq ($(VERSION),) + VERSION = 0.0.0-dev +endif + +$(BINARY): $(SOURCES) + CGO_ENABLED=0 go build -a -o ${BINARY} -ldflags \ "\ + -s -w \ + -X main.version=${VERSION} \ + -X main.commit=$(shell git show --format="%h" --no-patch) \ + -X main.date=$(shell date +%Y-%m-%dT%T%z)" + +.PHONY: build +build: $(BINARY) + +# +# Development +# + +BENCH ?= . +TESTARGS ?= + +.PHONY: clean +clean: + rm -f $(TOOLS) + rm -f ./coverage.out ./go.mod.tidy-check ./go.sum.tidy-check + +.PHONY: test +test: + go test $(V) -count=1 -race $(TESTARGS) ./... + +.PHONY: test-integration +test-integration: + env USE_ZFS=1 go test $(V) -count=1 $(TESTARGS) -run=^TestIntegration . + +.PHONY: test-deps +test-deps: + go test all + +.PHONY: lint +lint: $(TOOLDIR)/golangci-lint + golangci-lint $(V) run + +.PHONY: format +format: $(TOOLDIR)/goimports $(TOOLDIR)/gofumpt + goimports -w . && gofumpt -w . + +.SILENT: bench +.PHONY: bench +bench: + go test $(V) -count=1 -bench=$(BENCH) $(TESTARGS) ./... + +# +# Code Generation +# + +.PHONY: generate +generate: $(TOOLDIR)/mockgen + go generate ./... + +.PHONY: check-generate +check-generate: $(TOOLDIR)/mockgen + $(eval CHKDIR := $(shell mktemp -d)) + cp -av . "$(CHKDIR)" + make -C "$(CHKDIR)/" generate + ( diff -rN . "$(CHKDIR)" && rm -rf "$(CHKDIR)" ) || \ + ( rm -rf "$(CHKDIR)" && exit 1 ) + +# +# Coverage +# + +.PHONY: cov +cov: coverage.out + +.PHONY: cov-html +cov-html: coverage.out + go tool cover -html=./coverage.out + +.PHONY: cov-func +cov-func: coverage.out + go tool cover -func=./coverage.out + +coverage.out: $(SOURCES) + go test $(V) -covermode=count -coverprofile=./coverage.out ./... + +# +# Dependencies +# + +.PHONY: deps +deps: + go mod download + +.PHONY: deps-update +deps-update: + go get -u -t ./... + +.PHONY: deps-analyze +deps-analyze: $(TOOLDIR)/gomod + gomod analyze + +.PHONY: tidy +tidy: + go mod tidy $(V) + +.PHONY: verify +verify: + go mod verify + +.SILENT: check-tidy +.PHONY: check-tidy +check-tidy: + cp go.mod go.mod.tidy-check + cp go.sum go.sum.tidy-check + go mod tidy + ( \ + diff go.mod go.mod.tidy-check && \ + diff go.sum go.sum.tidy-check && \ + rm -f go.mod go.sum && \ + mv go.mod.tidy-check go.mod && \ + mv go.sum.tidy-check go.sum \ + ) || ( \ + rm -f go.mod go.sum && \ + mv go.mod.tidy-check go.mod && \ + mv go.sum.tidy-check go.sum; \ + exit 1 \ + ) + +# +# Documentation +# + +# Serve docs +.PHONY: docs +docs: $(TOOLDIR)/godoc + $(info serviing docs on http://127.0.0.1:6060/pkg/$(GOMODNAME)/) + @godoc -http=127.0.0.1:6060 + +# +# Release +# + +.PHONY: new-version +new-version: check-npx + npx standard-version + +.PHONY: next-version +next-version: check-npx + npx standard-version --dry-run + +.PHONY: check-npx +check-npx: + $(if $(shell which npx),,\ + $(error No npx found in PATH, please install NodeJS)) diff --git a/commands/midjourney.go b/commands/midjourney.go index 89fc2cc..b007e18 100644 --- a/commands/midjourney.go +++ b/commands/midjourney.go @@ -1,7 +1,7 @@ package commands import ( - "github.com/jimeh/mj2n/midjourney" + "github.com/jimeh/go-midjourney" "github.com/spf13/cobra" ) @@ -9,15 +9,22 @@ func NewMidjourney(mc *midjourney.Client) (*cobra.Command, error) { cmd := &cobra.Command{ Use: "midjourney", Aliases: []string{"mj"}, - Short: "MidJourney specific commands", + Short: "Query the Midjourney API directly", } recentJobsCmd, err := NewMidjourneyRecentJobs(mc) if err != nil { return nil, err } + wordsCmd, err := NewMidjourneyWords(mc) + if err != nil { + return nil, err + } - cmd.AddCommand(recentJobsCmd) + cmd.AddCommand( + recentJobsCmd, + wordsCmd, + ) return cmd, nil } diff --git a/commands/midjourney_recent_jobs.go b/commands/midjourney_recent_jobs.go index be5bee8..6e51dc7 100644 --- a/commands/midjourney_recent_jobs.go +++ b/commands/midjourney_recent_jobs.go @@ -1,7 +1,9 @@ package commands import ( - "github.com/jimeh/mj2n/midjourney" + "time" + + "github.com/jimeh/go-midjourney" "github.com/spf13/cobra" ) @@ -18,7 +20,8 @@ func NewMidjourneyRecentJobs(mc *midjourney.Client) (*cobra.Command, error) { cmd.Flags().StringP("type", "t", "", "type of jobs to list") cmd.Flags().StringP("order", "o", "new", "either \"new\" or \"oldest\"") cmd.Flags().StringP("user-id", "u", "", "user ID to list jobs for") - cmd.Flags().StringP("page", "p", "", "page to fetch") + cmd.Flags().IntP("page", "p", 0, "page to fetch") + cmd.Flags().StringP("prompt", "s", "", "prompt text to search for") cmd.Flags().Bool("dedupe", true, "dedupe results") return cmd, nil @@ -44,6 +47,9 @@ func midjourneyRecentJobsRunE(mc *midjourney.Client) runEFunc { if v, err := fs.GetInt("page"); err == nil && v != 0 { q.Page = v } + if v, err := fs.GetString("prompt"); err == nil && v != "" { + q.Prompt = v + } if v, err := fs.GetBool("dedupe"); err == nil { q.Dedupe = v } @@ -59,7 +65,7 @@ func midjourneyRecentJobsRunE(mc *midjourney.Client) runEFunc { ID: j.ID, Status: string(j.CurrentStatus), Type: string(j.Type), - EnqueueTime: j.EnqueueTime, + EnqueueTime: j.EnqueueTime.Time, Prompt: j.Prompt, ImagePaths: j.ImagePaths, IsPublished: j.IsPublished, @@ -76,15 +82,15 @@ func midjourneyRecentJobsRunE(mc *midjourney.Client) runEFunc { } type MidjourneyJob struct { - ID string `json:"id,omitempty" yaml:"id,omitempty"` - Status string `json:"current_status,omitempty" yaml:"current_status,omitempty"` - Type string `json:"type,omitempty" yaml:"type,omitempty"` - EnqueueTime string `json:"enqueue_time,omitempty" yaml:"enqueue_time,omitempty"` - Prompt string `json:"prompt,omitempty" yaml:"prompt,omitempty"` - ImagePaths []string `json:"image_paths,omitempty" yaml:"image_paths,omitempty"` - IsPublished bool `json:"is_published,omitempty" yaml:"is_published,omitempty"` - UserID string `json:"user_id,omitempty" yaml:"user_id,omitempty"` - Username string `json:"username,omitempty" yaml:"username,omitempty"` - FullCommand string `json:"full_command,omitempty" yaml:"full_command,omitempty"` - ReferenceJobID string `json:"reference_job_id,omitempty" yaml:"reference_job_id,omitempty"` + ID string `json:"id,omitempty" yaml:"id,omitempty"` + Status string `json:"current_status,omitempty" yaml:"current_status,omitempty"` + Type string `json:"type,omitempty" yaml:"type,omitempty"` + EnqueueTime time.Time `json:"enqueue_time,omitempty" yaml:"enqueue_time,omitempty"` + Prompt string `json:"prompt,omitempty" yaml:"prompt,omitempty"` + ImagePaths []string `json:"image_paths,omitempty" yaml:"image_paths,omitempty"` + IsPublished bool `json:"is_published,omitempty" yaml:"is_published,omitempty"` + UserID string `json:"user_id,omitempty" yaml:"user_id,omitempty"` + Username string `json:"username,omitempty" yaml:"username,omitempty"` + FullCommand string `json:"full_command,omitempty" yaml:"full_command,omitempty"` + ReferenceJobID string `json:"reference_job_id,omitempty" yaml:"reference_job_id,omitempty"` } diff --git a/commands/midjourney_words.go b/commands/midjourney_words.go new file mode 100644 index 0000000..5509686 --- /dev/null +++ b/commands/midjourney_words.go @@ -0,0 +1,70 @@ +package commands + +import ( + "sort" + + "github.com/jimeh/go-midjourney" + "github.com/spf13/cobra" +) + +func NewMidjourneyWords(mc *midjourney.Client) (*cobra.Command, error) { + cmd := &cobra.Command{ + Use: "words", + Aliases: []string{"w"}, + Short: "Get dictionary words", + RunE: midjourneyWordsRunE(mc), + } + + cmd.Flags().StringP("format", "f", "", "output format (yaml or json)") + cmd.Flags().StringP("query", "q", "", "query to search for") + cmd.Flags().IntP("amount", "a", 50, "amount of words to fetch") + cmd.Flags().IntP("page", "p", 0, "page to fetch") + cmd.Flags().IntP("seed", "s", 0, "seed") + + return cmd, nil +} + +func midjourneyWordsRunE(mc *midjourney.Client) runEFunc { + return func(cmd *cobra.Command, _ []string) error { + fs := cmd.Flags() + q := &midjourney.WordsQuery{} + + if v, err := fs.GetString("query"); err == nil && v != "" { + q.Query = v + } + if v, err := fs.GetInt("amount"); err == nil && v > 0 { + q.Amount = v + } + if v, err := fs.GetInt("page"); err == nil && v != 0 { + q.Page = v + } + if v, err := fs.GetInt("seed"); err == nil && v != 0 { + q.Seed = v + } + + words, err := mc.Words(cmd.Context(), q) + if err != nil { + return err + } + + r := []*MidjourneyWord{} + for _, w := range words { + r = append(r, &MidjourneyWord{ + Word: w.Word, + ImageURL: w.ImageURL(), + }) + } + + format := flagString(cmd, "format") + sort.SliceStable(r, func(i, j int) bool { + return r[i].Word < r[j].Word + }) + + return render(cmd.OutOrStdout(), format, r) + } +} + +type MidjourneyWord struct { + Word string `json:"word"` + ImageURL string `json:"image_url"` +} diff --git a/commands/mj2n.go b/commands/mje.go similarity index 57% rename from commands/mj2n.go rename to commands/mje.go index b49a132..823bc65 100644 --- a/commands/mj2n.go +++ b/commands/mje.go @@ -1,10 +1,11 @@ package commands import ( + "fmt" "io" "os" - "github.com/jimeh/mj2n/midjourney" + "github.com/jimeh/go-midjourney" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/spf13/cobra" @@ -12,27 +13,43 @@ import ( type runEFunc func(cmd *cobra.Command, _ []string) error -func NewMJ2N() (*cobra.Command, error) { - mc, err := midjourney.New(midjourney.WithUserAgent("mj2n/0.0.1-dev")) +type Info struct { + Version string + Commit string + Date string +} + +func New(info Info) (*cobra.Command, error) { + if info.Version == "" { + info.Version = "0.0.0-dev" + } + + mc, err := midjourney.New(midjourney.WithUserAgent("mje/" + info.Version)) if err != nil { return nil, err } cmd := &cobra.Command{ - Use: "mj2n", - Short: "MidJourney to Notion importer", + Use: "mje", + Short: "MidJourney exporter", + Version: info.Version, PersistentPreRunE: persistentPreRunE(mc), } - cmd.PersistentFlags().StringP( - "log-level", "l", "info", + cmd.PersistentFlags().String( + "log-level", "info", "one of: trace, debug, info, warn, error, fatal, panic", ) - cmd.PersistentFlags().StringP( - "mj-token", "m", "", "MidJourney API token", + cmd.PersistentFlags().String( + "log-format", "plain", + "one of: plain, json", ) cmd.PersistentFlags().String( - "mj-api-url", midjourney.DefaultAPIURL.String(), "MidJourney API URL", + "token", "", "MidJourney token", + ) + cmd.PersistentFlags().String( + "api-url", midjourney.DefaultAPIURL.String(), + "MidJourney API URL", ) midjourneyCmd, err := NewMidjourney(mc) @@ -66,13 +83,13 @@ func setupMidJourney(cmd *cobra.Command, mc *midjourney.Client) error { midjourney.WithLogger(log.Logger), } - if f := cmd.Flag("mj-token"); f.Changed { + if f := cmd.Flag("token"); f != nil && f.Changed { opts = append(opts, midjourney.WithAuthToken(f.Value.String())) } else if v := os.Getenv("MIDJOURNEY_TOKEN"); v != "" { opts = append(opts, midjourney.WithAuthToken(v)) } - apiURL := flagString(cmd, "mj-api-url") + apiURL := flagString(cmd, "api-url") if apiURL == "" { apiURL = os.Getenv("MIDJOURNEY_API_URL") } @@ -85,11 +102,16 @@ func setupMidJourney(cmd *cobra.Command, mc *midjourney.Client) error { func setupZerolog(cmd *cobra.Command) error { var levelStr string - if v := os.Getenv("MJ2N_DEBUG"); v != "" { + var logFormat string + + if v := os.Getenv("MJE_DEBUG"); v != "" { levelStr = "debug" - } else if v := os.Getenv("MJ2N_LOG_LEVEL"); v != "" { + } else if v := os.Getenv("MJE_LOG_LEVEL"); v != "" { levelStr = v } + if v := os.Getenv("MJE_LOG_FORMAT"); v != "" { + logFormat = v + } var out io.Writer = os.Stderr @@ -99,27 +121,33 @@ func setupZerolog(cmd *cobra.Command) error { if fl != nil && (fl.Changed || levelStr == "") { levelStr = fl.Value.String() } + + fl = cmd.Flag("log-format") + if fl != nil && (fl.Changed || logFormat == "") { + logFormat = fl.Value.String() + } } if levelStr == "" { levelStr = "info" } - level, err := zerolog.ParseLevel(levelStr) if err != nil { return err } - zerolog.SetGlobalLevel(level) - zerolog.TimeFieldFormat = "" - output := zerolog.ConsoleWriter{Out: out} - output.FormatTimestamp = func(i interface{}) string { - return "" + switch logFormat { + case "plain": + output := zerolog.ConsoleWriter{Out: out} + output.FormatTimestamp = func(i interface{}) string { return "" } + log.Logger = zerolog.New(output).With().Logger() + case "json": + log.Logger = zerolog.New(out).With().Timestamp().Logger() + default: + return fmt.Errorf("unknown log-format: %s", logFormat) } - log.Logger = zerolog.New(output).With().Timestamp().Logger() - return nil } diff --git a/commands/render.go b/commands/render.go index 896a5be..7c0d56e 100644 --- a/commands/render.go +++ b/commands/render.go @@ -34,7 +34,7 @@ func render(w io.Writer, format string, v interface{}) error { return err } - return renderYAML(w, v) + return renderJSON(w, v) } func renderYAML(w io.Writer, v interface{}) error { diff --git a/go.mod b/go.mod index 4079ab3..ada9730 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,9 @@ -module github.com/jimeh/mj2n +module github.com/jimeh/mje go 1.19 require ( + github.com/jimeh/go-midjourney v0.0.0-20220915223103-5d89204e21f0 github.com/rs/zerolog v1.28.0 github.com/spf13/cobra v1.5.0 gopkg.in/yaml.v3 v3.0.1 @@ -13,5 +14,5 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.16 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/sys v0.0.0-20220906165534-d0df966e6959 // indirect + golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41 // indirect ) diff --git a/go.sum b/go.sum index ca60b37..db33841 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,12 @@ github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jimeh/go-midjourney v0.0.0-20220915223103-5d89204e21f0 h1:xPHw47dnd//qDeUCBQ9XpJK6F+5XQFQJ2VP2AGldZto= +github.com/jimeh/go-midjourney v0.0.0-20220915223103-5d89204e21f0/go.mod h1:pQqr224bXA8yuiXxb5P1V/aNIiuhq2s8jBlT6P5tcMA= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= @@ -11,6 +14,7 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k 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/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= @@ -19,11 +23,12 @@ github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= 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/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220906165534-d0df966e6959 h1:qSa+Hg9oBe6UJXrznE+yYvW51V9UbyIj/nj/KpDigo8= -golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41 h1:ohgcoMbSofXygzo6AD2I1kz3BFmW1QArPYTtwEM3UXc= +golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/main.go b/main.go index 1c1db7d..9cf8cc8 100644 --- a/main.go +++ b/main.go @@ -7,11 +7,21 @@ import ( "os/signal" "syscall" - "github.com/jimeh/mj2n/commands" + "github.com/jimeh/mje/commands" +) + +var ( + version = "" + commit = "" + date = "" ) func main() { - cmd, err := commands.NewMJ2N() + cmd, err := commands.New(commands.Info{ + Version: version, + Commit: commit, + Date: date, + }) if err != nil { fatal(err) } diff --git a/midjourney/client.go b/midjourney/client.go deleted file mode 100644 index ecf6bd3..0000000 --- a/midjourney/client.go +++ /dev/null @@ -1,144 +0,0 @@ -package midjourney - -import ( - "errors" - "fmt" - "net/http" - "net/url" - "strings" - - "github.com/rs/zerolog" -) - -var ( - Err = errors.New("midjourney") - ErrNoAuthToken = fmt.Errorf("%w: no auth token", Err) - ErrInvalidAPIURL = fmt.Errorf("%w: invalid API URL", Err) - ErrInvalidHTTPClient = fmt.Errorf("%w: invalid HTTP client", Err) - ErrResponseStatus = fmt.Errorf("%w: response status", Err) - - DefaultAPIURL = url.URL{ - Scheme: "https", - Host: "www.midjourney.com", - Path: "/api/", - } - DefaultUserAgent = "go-midjourney/0.0.1-dev" -) - -type Order string - -const ( - OrderNew Order = "new" - OrderOldest Order = "oldest" -) - -type Option interface { - apply(*Client) error -} - -type optionFunc func(*Client) error - -func (fn optionFunc) apply(o *Client) error { - return fn(o) -} - -func WithAuthToken(authToken string) Option { - return optionFunc(func(c *Client) error { - c.AuthToken = authToken - - return nil - }) -} - -func WithAPIURL(baseURL string) Option { - return optionFunc(func(c *Client) error { - if !strings.HasSuffix(baseURL, "/") { - baseURL += "/" - } - - u, err := url.Parse(baseURL) - if err != nil { - return err - } - - c.APIURL = u - - return nil - }) -} - -func WithHTTPClient(httpClient *http.Client) Option { - return optionFunc(func(c *Client) error { - c.HTTPClient = httpClient - - return nil - }) -} - -func WithUserAgent(userAgent string) Option { - return optionFunc(func(c *Client) error { - c.UserAgent = userAgent - - return nil - }) -} - -func WithLogger(logger zerolog.Logger) Option { - return optionFunc(func(c *Client) error { - c.Logger = logger - - return nil - }) -} - -type HTTPClient interface { - Do(req *http.Request) (*http.Response, error) -} - -type Client struct { - HTTPClient HTTPClient - APIURL *url.URL - AuthToken string - UserAgent string - Logger zerolog.Logger -} - -func New(options ...Option) (*Client, error) { - c := &Client{ - HTTPClient: http.DefaultClient, - APIURL: &DefaultAPIURL, - UserAgent: DefaultUserAgent, - Logger: zerolog.Nop(), - } - err := c.Set(options...) - - return c, err -} - -func (c *Client) Set(options ...Option) error { - for _, opt := range options { - err := opt.apply(c) - if err != nil { - return err - } - } - - return nil -} - -func (c *Client) Do(req *http.Request) (*http.Response, error) { - req.URL = c.APIURL.ResolveReference(req.URL) - c.Logger.Debug().Str("url", req.URL.String()).Msg("request") - - req.Header.Set("Accept", "application/json") - if c.AuthToken != "" { - req.Header.Set( - "Cookie", "__Secure-next-auth.session-token="+c.AuthToken, - ) - } - if c.UserAgent != "" { - req.Header.Set("User-Agent", c.UserAgent) - } - - return c.HTTPClient.Do(req) -} diff --git a/midjourney/job.go b/midjourney/job.go deleted file mode 100644 index e75a8ca..0000000 --- a/midjourney/job.go +++ /dev/null @@ -1,57 +0,0 @@ -package midjourney - -type JobType string - -const ( - JobTypeGrid JobType = "grid" - JobTypeUpscale JobType = "upscale" -) - -type JobStatus string - -const ( - JobStatusRunning JobStatus = "running" - JobStatusCompleted JobStatus = "completed" -) - -type Event struct { - Height int `json:"height,omitempty"` - TextPrompt []string `json:"textPrompt,omitempty"` - ImagePrompts []string `json:"imagePrompts,omitempty"` - Width int `json:"width,omitempty"` - BatchSize int `json:"batchSize,omitempty"` - SeedImageURL string `json:"seedImageURL,omitempty"` -} - -type Job struct { - CurrentStatus JobStatus `json:"current_status,omitempty"` - EnqueueTime string `json:"enqueue_time,omitempty"` - Event *Event `json:"event,omitempty"` - Flagged bool `json:"flagged,omitempty"` - FollowedByUser bool `json:"followed_by_user,omitempty"` - GridID string `json:"grid_id,omitempty"` - GridNum string `json:"grid_num,omitempty"` - GuildID string `json:"guild_id,omitempty"` - Hidden bool `json:"hidden,omitempty"` - ID string `json:"id,omitempty"` - ImagePaths []string `json:"image_paths,omitempty"` - IsPublished bool `json:"is_published,omitempty"` - LikedByUser bool `json:"liked_by_user,omitempty"` - LowPriority bool `json:"low_priority,omitempty"` - Metered bool `json:"metered,omitempty"` - ModHidden bool `json:"mod_hidden,omitempty"` - Platform string `json:"platform,omitempty"` - PlatformChannel string `json:"platform_channel,omitempty"` - PlatformChannelID string `json:"platform_channel_id,omitempty"` - PlatformMessageID string `json:"platform_message_id,omitempty"` - PlatformThreadID string `json:"platform_thread_id,omitempty"` - Prompt string `json:"prompt,omitempty"` - RankedByUser bool `json:"ranked_by_user,omitempty"` - RankingByUser int `json:"ranking_by_user,omitempty"` - Type JobType `json:"type,omitempty"` - UserID string `json:"user_id,omitempty"` - Username string `json:"username,omitempty"` - FullCommand string `json:"full_command,omitempty"` - ReferenceJobID string `json:"reference_job_id,omitempty"` - ReferenceImageNum string `json:"reference_image_num,omitempty"` -} diff --git a/midjourney/midjourney.go b/midjourney/midjourney.go deleted file mode 100644 index 28503b7..0000000 --- a/midjourney/midjourney.go +++ /dev/null @@ -1,4 +0,0 @@ -// Package midjourney provides a basic read-only API client for MidJourney. As -// there is no official API, it uses the same API as the MidJourney website -// uses. -package midjourney diff --git a/midjourney/recent_jobs.go b/midjourney/recent_jobs.go deleted file mode 100644 index 1e5df5f..0000000 --- a/midjourney/recent_jobs.go +++ /dev/null @@ -1,124 +0,0 @@ -package midjourney - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "net/url" - "strconv" - "time" -) - -const FromDateFormat = "2006-01-02 15:04:05.999999" - -type RecentJobsQuery struct { - Amount int - JobType JobType - OrderBy Order - JobStatus JobStatus - UserID string - FromDate time.Time - Page int - Dedupe bool - RefreshAPI int -} - -func (rjq *RecentJobsQuery) Values() url.Values { - v := url.Values{} - if rjq.Amount != 0 { - v.Set("amount", strconv.Itoa(rjq.Amount)) - } - if rjq.JobType != "" { - v.Set("jobType", string(rjq.JobType)) - } - if rjq.OrderBy != "" { - v.Set("orderBy", string(rjq.OrderBy)) - } - if rjq.JobStatus != "" { - v.Set("jobStatus", string(rjq.JobStatus)) - } - if rjq.UserID != "" { - v.Set("userId", rjq.UserID) - } - if !rjq.FromDate.IsZero() { - v.Set("fromDate", rjq.FromDate.Format(FromDateFormat)) - } - if rjq.Page != 0 { - v.Set("page", strconv.Itoa(rjq.Page)) - } - if rjq.Dedupe { - v.Set("dedupe", "true") - } - if rjq.RefreshAPI != 0 { - v.Set("refreshApi", strconv.Itoa(rjq.RefreshAPI)) - } - - return v -} - -func (rjq *RecentJobsQuery) NextPage() *RecentJobsQuery { - q := *rjq - if q.OrderBy == OrderNew && q.FromDate.IsZero() { - q.FromDate = time.Now().UTC() - } - if q.Page == 0 { - q.Page = 1 - } - q.Page = rjq.Page + 1 - - return &q -} - -type RecentJobs struct { - Query RecentJobsQuery - Jobs []*Job - Page int -} - -func (c *Client) RecentJobs( - ctx context.Context, - q *RecentJobsQuery, -) (*RecentJobs, error) { - u := &url.URL{ - Path: "app/recent-jobs", - RawQuery: q.Values().Encode(), - } - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) - if err != nil { - return nil, err - } - - fromDate := q.FromDate - if fromDate.IsZero() { - fromDate = time.Now().UTC() - } - - resp, err := c.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%w: %s", ErrResponseStatus, resp.Status) - } - - rj := &RecentJobs{ - Query: *q, - Jobs: []*Job{}, - Page: q.Page, - } - - err = json.NewDecoder(resp.Body).Decode(&rj.Jobs) - if err != nil { - return nil, err - } - - if rj.Query.OrderBy == OrderNew && rj.Query.FromDate.IsZero() { - rj.Query.FromDate = fromDate - } - - return rj, nil -}