diff --git a/CHANGELOG.md b/CHANGELOG.md index 2db70e48..9d689d9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org). +## 3.11.0 + +- Add an option to search mangas with inline mode. `mangal inline -q "..." -j` will output search results without chapters. #97 +- `config` cmd improved. Now, `config set` will automatically parse the value to the expected type. +- Internal improvements. + + ## 3.10.0 - New feature: you can choose what anilist manga to link by pressing a in the manga chapters list. diff --git a/cmd/config.go b/cmd/config.go index cbb09150..831094c3 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -14,129 +14,116 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "path/filepath" - "reflect" - "sort" "strconv" ) -func init() { - rootCmd.AddCommand(configCmd) - configCmd.AddCommand(configInitCmd) - configInitCmd.Flags().BoolP("force", "f", false, "overwrite existing config") - - configCmd.AddCommand(configRemoveCmd) - configCmd.AddCommand(configSetCmd) - configSetCmd.Flags().StringP("key", "k", "", "key to set") - configSetCmd.Flags().StringP("value", "v", "", "value to set") - configSetCmd.Flags().BoolP("bool", "b", false, "value is a boolean") - configSetCmd.Flags().BoolP("int", "i", false, "value is an integer") - - lo.Must0(configSetCmd.MarkFlagRequired("key")) - configSetCmd.MarkFlagsMutuallyExclusive("bool", "int") - lo.Must0(configSetCmd.MarkFlagRequired("value")) - lo.Must0(configSetCmd.RegisterFlagCompletionFunc("key", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return lo.Keys(config.DefaultValues), cobra.ShellCompDirectiveNoFileComp - })) - - configCmd.AddCommand(configGetCmd) - configGetCmd.Flags().StringP("key", "k", "", "key to get") - lo.Must0(configGetCmd.MarkFlagRequired("key")) - lo.Must0(configGetCmd.RegisterFlagCompletionFunc("key", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return lo.Keys(config.DefaultValues), cobra.ShellCompDirectiveNoFileComp - })) - - configCmd.AddCommand(configInfoCmd) - configInfoCmd.Flags().StringP("key", "k", "", "show only this key") - lo.Must0(configInfoCmd.RegisterFlagCompletionFunc("key", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return lo.Keys(config.DefaultValues), cobra.ShellCompDirectiveNoFileComp - })) -} - func errUnknownKey(key string) error { - closest := lo.MinBy(lo.Keys(config.DefaultValues), func(a string, b string) bool { + closest := lo.MinBy(lo.Keys(config.Default), func(a string, b string) bool { return levenshtein.Distance(key, a) < levenshtein.Distance(key, b) }) - msg := fmt.Sprintf(`unknown key %s, did you mean %s?`, style.Red(key), style.Yellow(closest)) + msg := fmt.Sprintf( + "unknown key %s, did you mean %s?", + style.Red(key), + style.Yellow(closest), + ) + return errors.New(msg) } +func completionConfigKeys(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { + return lo.Keys(config.Default), cobra.ShellCompDirectiveNoFileComp +} + +func init() { + rootCmd.AddCommand(configCmd) +} + var configCmd = &cobra.Command{ Use: "config", - Short: "Various config actions", - Long: `Various config actions`, + Short: "Various config commands", +} + +func init() { + configCmd.AddCommand(configInfoCmd) + configInfoCmd.Flags().StringP("key", "k", "", "The key to get the value for") + _ = configInfoCmd.RegisterFlagCompletionFunc("key", completionConfigKeys) } -var configInitCmd = &cobra.Command{ - Use: "init", - Short: "Initialize config", - Long: `Initialize default config`, +var configInfoCmd = &cobra.Command{ + Use: "info", + Short: "Show the info for each config field with description", Run: func(cmd *cobra.Command, args []string) { - force := lo.Must(cmd.Flags().GetBool("force")) - if force { - err := filesystem.Api().Remove(filepath.Join(where.Config(), "mangal.toml")) - handleErr(err) + var ( + key = lo.Must(cmd.Flags().GetString("key")) + fields = lo.Values(config.Default) + ) + + if key != "" { + if field, ok := config.Default[key]; ok { + fields = []config.Field{field} + } else { + handleErr(errUnknownKey(key)) + } } - handleErr(viper.SafeWriteConfig()) + for i, field := range fields { + fmt.Print(field.Pretty()) + + if i < len(fields)-1 { + fmt.Println() + } + } }, } -var configRemoveCmd = &cobra.Command{ - Use: "remove", - Short: "Removes config file", - Run: func(cmd *cobra.Command, args []string) { - mangalDir := where.Config() - configPath := filepath.Join(mangalDir, constant.Mangal+".toml") +func init() { + configCmd.AddCommand(configSetCmd) + configSetCmd.Flags().StringP("key", "k", "", "The key to set the value for") + lo.Must0(configSetCmd.MarkFlagRequired("key")) + _ = configSetCmd.RegisterFlagCompletionFunc("key", completionConfigKeys) - if lo.Must(filesystem.Api().Exists(configPath)) { - handleErr(filesystem.Api().Remove(configPath)) - } - }, + configSetCmd.Flags().StringP("value", "v", "", "The value to set") + lo.Must0(configSetCmd.MarkFlagRequired("value")) + + // deprecated flags for backwards compatibility + configSetCmd.Flags().BoolP("bool", "b", false, "Set the value type to bool") + configSetCmd.Flags().IntP("int", "i", 0, "Set the value type to int") } var configSetCmd = &cobra.Command{ Use: "set", - Short: "Set config value", - Long: `Set config value. Example: -mangal config set --key "formats.use" --value cbz -`, + Short: "Set a config value", Run: func(cmd *cobra.Command, args []string) { - key := lo.Must(cmd.Flags().GetString("key")) - isBool := lo.Must(cmd.Flags().GetBool("bool")) - isInt := lo.Must(cmd.Flags().GetBool("int")) - - v := lo.Must(cmd.Flags().GetString("value")) - - var value any - - if isBool { - if v == "true" { - value = true - } else if v == "false" { - value = false - } else { - handleErr(fmt.Errorf("invalid boolean value %s", style.Yellow(v))) - } - } else if isInt { - var err error - value, err = strconv.Atoi(v) - handleErr(err) - } else { - value = v - } + var ( + key = lo.Must(cmd.Flags().GetString("key")) + value = lo.Must(cmd.Flags().GetString("value")) + ) - if _, ok := config.DefaultValues[key]; !ok { + if _, ok := config.Default[key]; !ok { handleErr(errUnknownKey(key)) } - expectedType := reflect.TypeOf(config.DefaultValues[key].Value) - actualType := reflect.TypeOf(value) + var v any + switch config.Default[key].Value.(type) { + case string: + v = value + case int: + parsedInt, err := strconv.ParseInt(value, 10, 64) + if err != nil { + handleErr(fmt.Errorf("invalid integer value: %s", value)) + } - if expectedType != actualType { - handleErr(fmt.Errorf(`expected type %s but got %s`, style.Blue(expectedType.String()), style.Red(actualType.String()))) + v = int(parsedInt) + case bool: + parsedBool, err := strconv.ParseBool(value) + if err != nil { + handleErr(fmt.Errorf("invalid boolean value: %s", value)) + } + + v = parsedBool } - viper.Set(key, value) + viper.Set(key, v) switch err := viper.WriteConfig(); err.(type) { case viper.ConfigFileNotFoundError: handleErr(viper.SafeWriteConfig()) @@ -144,19 +131,31 @@ mangal config set --key "formats.use" --value cbz handleErr(err) } - fmt.Printf("%s set %s to %s\n", icon.Get(icon.Success), style.Magenta(key), style.Yellow(v)) + fmt.Printf( + "%s set %s to %s\n", + style.Green(icon.Get(icon.Success)), + style.Magenta(key), + style.Yellow(fmt.Sprintf("%v", v)), + ) }, } +func init() { + configCmd.AddCommand(configGetCmd) + configGetCmd.Flags().StringP("key", "k", "", "The key to get the value for") + lo.Must0(configGetCmd.MarkFlagRequired("key")) + _ = configGetCmd.RegisterFlagCompletionFunc("key", completionConfigKeys) +} + var configGetCmd = &cobra.Command{ Use: "get", - Short: "Get config value", - Long: `Get config value. Example: -mangal config get --key "formats.use" -`, + Short: "Get a config value", Run: func(cmd *cobra.Command, args []string) { - key := lo.Must(cmd.Flags().GetString("key")) - if _, ok := config.DefaultValues[key]; !ok { + var ( + key = lo.Must(cmd.Flags().GetString("key")) + ) + + if _, ok := config.Default[key]; !ok { handleErr(errUnknownKey(key)) } @@ -164,36 +163,62 @@ mangal config get --key "formats.use" }, } -var configInfoCmd = &cobra.Command{ - Use: "info", - Short: "List all config values with their types and descriptions", +func init() { + configCmd.AddCommand(configWriteCmd) + configWriteCmd.Flags().BoolP("force", "f", false, "Force overwrite of existing config file") +} + +var configWriteCmd = &cobra.Command{ + Use: "write", + Short: "Write current config to the file", Run: func(cmd *cobra.Command, args []string) { var ( - fields = make([]config.Field, len(config.DefaultValues)) - key = lo.Must(cmd.Flags().GetString("key")) + force = lo.Must(cmd.Flags().GetBool("force")) + configFilePath = filepath.Join( + where.Config(), + fmt.Sprintf("%s.%s", constant.Mangal, "toml"), + ) ) - i := 0 - for _, v := range config.DefaultValues { - fields[i] = v - i++ - } + if force { + err := filesystem. + Api(). + Remove(configFilePath) - if key != "" { - if field, ok := config.DefaultValues[key]; ok { - fmt.Print(field.Pretty()) - return - } - handleErr(errUnknownKey(key)) + handleErr(err) } - sort.Slice(fields, func(i, j int) bool { - return fields[i].Name < fields[j].Name - }) + handleErr(viper.SafeWriteConfig()) + fmt.Printf( + "%s wrote config to %s\n", + style.Green(icon.Get(icon.Success)), + configFilePath, + ) + }, +} - for _, field := range fields { - // extra newline - fmt.Printf("%s\n", field.Pretty()) - } +func init() { + configCmd.AddCommand(configDeleteCmd) +} + +var configDeleteCmd = &cobra.Command{ + Use: "delete", + Short: "Delete the config file", + Aliases: []string{"remove"}, + Run: func(cmd *cobra.Command, args []string) { + err := filesystem. + Api(). + Remove( + filepath.Join( + where.Config(), + fmt.Sprintf("%s.%s", constant.Mangal, "toml"), + ), + ) + + handleErr(err) + fmt.Printf( + "%s deleted config\n", + style.Green(icon.Get(icon.Success)), + ) }, } diff --git a/cmd/inline.go b/cmd/inline.go index cda509c2..1a62b538 100644 --- a/cmd/inline.go +++ b/cmd/inline.go @@ -25,10 +25,12 @@ func init() { inlineCmd.Flags().BoolP("download", "d", false, "download chapters") inlineCmd.Flags().BoolP("json", "j", false, "JSON output") inlineCmd.Flags().BoolP("populate-pages", "p", false, "Populate chapters pages") + inlineCmd.Flags().BoolP("fetch-metadata", "f", false, "Populate manga metadata") + lo.Must0(viper.BindPFlag(constant.MetadataFetchAnilist, inlineCmd.Flags().Lookup("fetch-metadata"))) + inlineCmd.Flags().StringP("output", "o", "", "output file") lo.Must0(inlineCmd.MarkFlagRequired("query")) - lo.Must0(inlineCmd.MarkFlagRequired("chapters")) inlineCmd.MarkFlagsMutuallyExclusive("download", "json") } @@ -98,8 +100,13 @@ When using the json flag manga selector could be omitted. That way, it will sele mangaPicker = util.Some(fn) } - chapterFilter, err := inline.ParseChaptersFilter(lo.Must(cmd.Flags().GetString("chapters"))) - handleErr(err) + chapterFlag := lo.Must(cmd.Flags().GetString("chapters")) + chapterFilter := util.None[inline.ChaptersFilter]() + if chapterFlag != "" { + fn, err := inline.ParseChaptersFilter(chapterFlag) + handleErr(err) + chapterFilter = util.Some(fn) + } options := &inline.Options{ Source: src, diff --git a/cmd/where.go b/cmd/where.go index 309770a0..b6a306b7 100644 --- a/cmd/where.go +++ b/cmd/where.go @@ -1,79 +1,57 @@ package cmd import ( + "os" + + "github.com/metafates/mangal/constant" "github.com/metafates/mangal/style" + "github.com/metafates/mangal/util" "github.com/metafates/mangal/where" "github.com/samber/lo" "github.com/spf13/cobra" - "os" ) +var wherePaths = []lo.Tuple2[string, func() string]{ + {"downloads", where.Downloads}, + {"config", where.Config}, + {"sources", where.Sources}, + {"logs", where.Logs}, +} + func init() { rootCmd.AddCommand(whereCmd) - whereCmd.Flags().BoolP("config", "c", false, "configuration path") - whereCmd.Flags().BoolP("sources", "s", false, "sources path") - whereCmd.Flags().BoolP("logs", "l", false, "logs path") - whereCmd.Flags().BoolP("downloads", "d", false, "downloads path") - whereCmd.MarkFlagsMutuallyExclusive("config", "sources", "logs", "downloads") + for _, n := range wherePaths { + whereCmd.Flags().BoolP(n.A, string(n.A[0]), false, n.A+" path") + } + + whereCmd.MarkFlagsMutuallyExclusive(lo.Map(wherePaths, func(t lo.Tuple2[string, func() string], _ int) string { + return t.A + })...) + + whereCmd.SetOut(os.Stdout) } var whereCmd = &cobra.Command{ Use: "where", - Short: "Show the paths for a files related to the mangal", + Short: "Show the paths for a files related to the " + constant.Mangal, Run: func(cmd *cobra.Command, args []string) { - cmd.SetOut(os.Stdout) - - headerStyle := style.Combined(style.Bold, style.HiBlue) - - whereConfig := lo.Must(cmd.Flags().GetBool("config")) - whereSources := lo.Must(cmd.Flags().GetBool("sources")) - whereLogs := lo.Must(cmd.Flags().GetBool("logs")) - whereDownloads := lo.Must(cmd.Flags().GetBool("downloads")) + headerStyle := style.Combined(style.Bold, style.HiMagenta) - title := func(do bool, what, arg string) { - if do { - cmd.Printf("%s %s\n", headerStyle(what+"?"), style.Yellow(arg)) + for _, n := range wherePaths { + if lo.Must(cmd.Flags().GetBool(n.A)) { + cmd.Println(n.B()) + return } } - printConfigPath := func(header bool) { - title(header, "Configuration", "--config") - cmd.Println(where.Config()) - } - - printSourcesPath := func(header bool) { - title(header, "Sources", "--sources") - cmd.Println(where.Sources()) - } - - printLogsPath := func(header bool) { - title(header, "Logs", "--logs") - cmd.Println(where.Logs()) - } - - printDownloadsPath := func(header bool) { - title(header, "Downloads", "--downloads") - cmd.Println(where.Downloads()) - } + for i, n := range wherePaths { + cmd.Printf("%s %s\n", headerStyle(util.Capitalize(n.A)+"?"), style.Yellow("--"+n.A)) + cmd.Println(n.B()) - switch { - case whereConfig: - printConfigPath(false) - case whereSources: - printSourcesPath(false) - case whereLogs: - printLogsPath(false) - case whereDownloads: - printDownloadsPath(false) - default: - printConfigPath(true) - cmd.Println() - printSourcesPath(true) - cmd.Println() - printLogsPath(true) - cmd.Println() - printDownloadsPath(true) + if i < len(wherePaths)-1 { + cmd.Println() + } } }, } diff --git a/config/config.go b/config/config.go index 2f4fe848..c6b6bc4e 100644 --- a/config/config.go +++ b/config/config.go @@ -64,7 +64,7 @@ func setEnvs() { func setDefaults() { viper.SetTypeByDefaultValue(true) - for name, field := range DefaultValues { + for name, field := range Default { viper.SetDefault(name, field.Value) } } diff --git a/config/default.go b/config/default.go index a3a6e8ea..27c749dd 100644 --- a/config/default.go +++ b/config/default.go @@ -278,9 +278,9 @@ panic, fatal, error, warn, info, debug, trace`, } for _, field := range fields { - DefaultValues[field.Name] = field + Default[field.Name] = field EnvExposed = append(EnvExposed, field.Name) } } -var DefaultValues = make(map[string]Field) +var Default = make(map[string]Field) diff --git a/constant/constant.go b/constant/constant.go index d9a76263..c028f747 100644 --- a/constant/constant.go +++ b/constant/constant.go @@ -2,7 +2,7 @@ package constant const ( Mangal = "mangal" - Version = "3.10.0" + Version = "3.11.0" UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36" ) diff --git a/go.mod b/go.mod index df3c8b46..60f5ed47 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/spf13/cobra v1.5.0 github.com/spf13/viper v1.13.0 github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 - golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e + golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 golang.org/x/term v0.0.0-20220919170432-7a66f970e087 ) @@ -33,12 +33,13 @@ require ( github.com/antchfx/xmlquery v1.3.12 // indirect github.com/antchfx/xpath v1.2.1 // indirect github.com/atotto/clipboard v0.1.4 // indirect + github.com/aymanbagabas/go-osc52 v1.0.3 // indirect github.com/cbroglie/mustache v1.4.0 // indirect github.com/charmbracelet/harmonica v0.2.0 // indirect github.com/containerd/console v1.0.3 // indirect github.com/fatih/color v1.13.0 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect - github.com/go-rod/rod v0.109.3 // indirect + github.com/go-rod/rod v0.111.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect @@ -62,7 +63,7 @@ require ( github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect - github.com/muesli/termenv v0.12.0 // indirect + github.com/muesli/termenv v0.13.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/pkg/errors v0.9.1 // indirect @@ -80,8 +81,8 @@ require ( github.com/ysmood/leakless v0.8.0 // indirect github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7 // indirect golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 // indirect - golang.org/x/net v0.0.0-20220920203100-d0c6ba3f52d9 // indirect - golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect + golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b // indirect + golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect golang.org/x/text v0.3.7 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.1 // indirect diff --git a/go.sum b/go.sum index 00649cc6..fd024456 100644 --- a/go.sum +++ b/go.sum @@ -60,6 +60,8 @@ github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= 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 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= github.com/cbroglie/mustache v1.4.0 h1:Azg0dVhxTml5me+7PsZ7WPrQq1Gkf3WApcHMjMprYoU= github.com/cbroglie/mustache v1.4.0/go.mod h1:SS1FTIghy0sjse4DUVGV1k/40B1qE1XkD9DtDsHo9iM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -112,6 +114,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-rod/rod v0.109.3 h1:MxuSJGK9lEUq07K+QPfnxnuvQpsQT+YI4SoQjSE0LVg= github.com/go-rod/rod v0.109.3/go.mod h1:GZDtmEs6RpF6kBRYpGCZXxXlKNneKVPiKOjaMbmVVjE= +github.com/go-rod/rod v0.111.0 h1:aMNNdz10GYPYec9z1WsFqwAdRYVsuufVTOrah7whG3I= +github.com/go-rod/rod v0.111.0/go.mod h1:GZDtmEs6RpF6kBRYpGCZXxXlKNneKVPiKOjaMbmVVjE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gocolly/colly v1.2.0 h1:qRz9YAn8FIH0qzgNUw+HT9UN7wm1oF9OBAilwEWpyrI= @@ -259,6 +263,8 @@ github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= github.com/muesli/termenv v0.12.0 h1:KuQRUE3PgxRFWhq4gHvZtPSLCGDqM5q/cYr1pZ39ytc= github.com/muesli/termenv v0.12.0/go.mod h1:WCCv32tusQ/EEZ5S8oUIIrC/nIuBcxCVqlN4Xfkv+7A= +github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0= +github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= github.com/pdfcpu/pdfcpu v0.3.13 h1:VFon2Yo1PJt+sA57vPAeXWGLSZ7Ux3Jl4h02M0+s3dg= github.com/pdfcpu/pdfcpu v0.3.13/go.mod h1:UJc5xsXg0fpmjp1zOPdyYcAQArc/Zf3V0nv5URe+9fg= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= @@ -365,6 +371,10 @@ golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b h1:SCE/18RnFsLrjydh/R/s5EVvH golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e h1:Ctm9yurWsg7aWwIpH9Bnap/IdSVxixymIb3MhiMEQQA= golang.org/x/exp v0.0.0-20220921023135-46d9e7742f1e/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20220929160808-de9c53c655b9 h1:lNtcVz/3bOstm7Vebox+5m3nLh/BYWnhmc3AhXOW6oI= +golang.org/x/exp v0.0.0-20220929160808-de9c53c655b9/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 h1:RjggHMcaTVp0LOVZcW0bo8alwHrOaCrGUDgfWUHhnN4= +golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190823064033-3a9bac650e44/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -429,6 +439,10 @@ golang.org/x/net v0.0.0-20220920191752-2e0b12c274b7 h1:DTGA3sVb/sQX+3poldfq5cO4K golang.org/x/net v0.0.0-20220920191752-2e0b12c274b7/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220920203100-d0c6ba3f52d9 h1:asZqf0wXastQr+DudYagQS8uBO8bHKeYD1vbAvGmFL8= golang.org/x/net v0.0.0-20220920203100-d0c6ba3f52d9/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220927171203-f486391704dc h1:FxpXZdoBqT8RjqTy6i1E8nXHhW21wK7ptQ/EPIGxzPQ= +golang.org/x/net v0.0.0-20220927171203-f486391704dc/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b h1:uKO3Js8lXGjpjdc4J3rqs0/Ex5yDKUGfk43tTYWVLas= +golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 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-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -496,6 +510,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc= golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI= +golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/inline/inline.go b/inline/inline.go index d0f0c7d7..e6cd0bcf 100644 --- a/inline/inline.go +++ b/inline/inline.go @@ -19,6 +19,22 @@ func Run(options *Options) error { return err } + if options.MangaPicker.IsNone() && options.ChaptersFilter.IsNone() { + if viper.GetBool(constant.MetadataFetchAnilist) { + for _, manga := range mangas { + _ = manga.PopulateMetadata(func(string) {}) + } + } + + marshalled, err := asJson(mangas) + if err != nil { + return err + } + + _, err = options.Out.Write(marshalled) + return err + } + // manga picker can only be none if json is set if options.MangaPicker.IsNone() { // preload all chapters @@ -49,9 +65,11 @@ func Run(options *Options) error { return err } - chapters, err = options.ChaptersFilter(chapters) - if err != nil { - return err + if options.ChaptersFilter.IsSome() { + chapters, err = options.ChaptersFilter.Unwrap()(chapters) + if err != nil { + return err + } } if options.Json { diff --git a/inline/json.go b/inline/json.go index 26c33552..caa60de4 100644 --- a/inline/json.go +++ b/inline/json.go @@ -16,11 +16,13 @@ func asJson(manga []*source.Manga) (marshalled []byte, err error) { } func jsonUpdateChapters(manga *source.Manga, options *Options) error { + var err error chapters, _ := options.Source.ChaptersOf(manga) - chapters, err := options.ChaptersFilter(chapters) - - if err != nil { - return err + if options.ChaptersFilter.IsSome() { + chapters, err = options.ChaptersFilter.Unwrap()(chapters) + if err != nil { + return err + } } manga.Chapters = chapters diff --git a/inline/options.go b/inline/options.go index 06c1faf1..39fcfb60 100644 --- a/inline/options.go +++ b/inline/options.go @@ -24,7 +24,7 @@ type Options struct { PopulatePages bool Query string MangaPicker util.Option[MangaPicker] - ChaptersFilter ChaptersFilter + ChaptersFilter util.Option[ChaptersFilter] } func ParseMangaPicker(description string) (MangaPicker, error) { diff --git a/mini/states.go b/mini/states.go index b020141a..5bb1909e 100644 --- a/mini/states.go +++ b/mini/states.go @@ -46,15 +46,9 @@ func (m *mini) handleSourceSelectState() error { defaultProviders := provider.DefaultProviders() customProviders := provider.CustomProviders() - var providers = make([]*provider.Provider, 0) - - for _, p := range defaultProviders { - providers = append(providers, p) - } - - for _, p := range customProviders { - providers = append(providers, p) - } + var providers = make([]*provider.Provider, len(defaultProviders)+len(customProviders)) + providers = append(providers, provider.DefaultProviders()...) + providers = append(providers, provider.CustomProviders()...) slices.SortFunc(providers, func(a *provider.Provider, b *provider.Provider) bool { return strings.Compare(a.String(), b.String()) < 0 @@ -389,15 +383,9 @@ func (m *mini) handleHistorySelectState() error { defaultProviders := provider.DefaultProviders() customProviders := provider.CustomProviders() - var providers = make([]*provider.Provider, 0) - - for _, p := range defaultProviders { - providers = append(providers, p) - } - - for _, p := range customProviders { - providers = append(providers, p) - } + var providers = make([]*provider.Provider, len(defaultProviders)+len(customProviders)) + providers = append(providers, defaultProviders...) + providers = append(providers, customProviders...) p, _ := lo.Find(providers, func(p *provider.Provider) bool { return p.ID == c.SourceID diff --git a/scripts/run.ps1 b/scripts/run.ps1 index 607df67f..504547fe 100644 --- a/scripts/run.ps1 +++ b/scripts/run.ps1 @@ -27,9 +27,14 @@ if (Test-Path -path $loc) } Write-Host "Downloading Mangal version $tag" -ForegroundColor DarkCyan -Invoke-WebRequest $url -outfile mangal.zip -Expand-Archive mangal.zip -.\mangal\mangal.exe +# download mangal to temp folder +$zip = "$env:TEMP\mangal.zip" +Invoke-WebRequest -Uri $url -OutFile $zip -Remove-Item mangal* -Recurse -Force +# extract mangal at temp folder +Expand-Archive -Path $zip -DestinationPath $env:TEMP + +# run mangal binary from the unzipped folder +$bin = "$env:TEMP\mangal\mangal.exe" +Start-Process $bin diff --git a/source/manga.go b/source/manga.go index a70feb6e..3e208003 100644 --- a/source/manga.go +++ b/source/manga.go @@ -196,16 +196,15 @@ func (m *Manga) PopulateMetadata(progress func(string)) error { m.Metadata.Synonyms = manga.Synonyms urls := []string{manga.URL} - if manga.SiteURL != "" { - urls = append(urls, manga.SiteURL) - } - + urls = append(urls, manga.SiteURL) for _, e := range manga.External { - if e.URL != "" { - urls = append(urls, e.URL) - } + urls = append(urls, e.URL) } + urls = lo.Filter(urls, func(url string, _ int) bool { + return url != "" + }) + urls = append(urls, fmt.Sprintf("https://myanimelist.net/manga/%d", manga.IDMal)) m.Metadata.URLs = urls diff --git a/tui/bubble.go b/tui/bubble.go index f62f024b..c4f9a91f 100644 --- a/tui/bubble.go +++ b/tui/bubble.go @@ -48,7 +48,6 @@ type statefulBubble struct { selectedManga *source.Manga selectedChapters map[*source.Chapter]struct{} // mathematical set - cancelCurrentOperation bool scrapersLoadedChannel chan []*installer.Scraper scraperInstalledChannel chan *installer.Scraper sourcesLoadedChannel chan []source.Source