1
0
mirror of https://github.com/coalaura/ffwebp.git synced 2025-12-02 18:22:53 +00:00

patterns and help topics

This commit is contained in:
Laura
2025-10-08 23:26:48 +02:00
parent 96937f04fa
commit ff0f9c9a87
4 changed files with 193 additions and 3 deletions

View File

@@ -14,6 +14,7 @@ import (
_ "github.com/coalaura/ffwebp/internal/builtins"
"github.com/coalaura/ffwebp/internal/codec"
"github.com/coalaura/ffwebp/internal/effects"
"github.com/coalaura/ffwebp/internal/help"
"github.com/coalaura/ffwebp/internal/logx"
"github.com/coalaura/ffwebp/internal/opts"
"github.com/nfnt/resize"
@@ -33,7 +34,7 @@ func main() {
&cli.StringFlag{
Name: "output",
Aliases: []string{"o"},
Usage: "output file, directory, or pattern (\"-\" = stdout)",
Usage: "output file, directory, or pattern (\"-\" = stdout, supports %d and templates)",
Value: "-",
},
&cli.IntFlag{
@@ -104,6 +105,9 @@ func main() {
EnableShellCompletion: true,
UseShortOptionHandling: true,
Suggest: true,
Commands: []*cli.Command{
help.Command(),
},
}
if err := app.Run(context.Background(), os.Args); err != nil {
@@ -164,11 +168,29 @@ func run(_ context.Context, cmd *cli.Command) error {
var outputs []string
if len(inputs) == 1 {
outputs = []string{output}
out := output
if out != "-" {
if hasTemplate(out) {
out = formatTemplate(out, inputs[0], 0, startNum)
} else if hasSeq(out) {
out = formatSeq(out, 0, startNum)
}
}
outputs = []string{out}
} else {
switch {
case output == "-":
return fmt.Errorf("multiple inputs require an output pattern or directory, not '-' ")
case hasTemplate(output):
outs := make([]string, len(inputs))
for i := range inputs {
outs[i] = formatTemplate(output, inputs[i], i, startNum)
}
outputs = outs
case hasSeq(output):
outs := make([]string, len(inputs))
@@ -196,7 +218,7 @@ func run(_ context.Context, cmd *cli.Command) error {
outs := make([]string, len(inputs))
for i := range inputs {
outs[i] = output
outs[i] = outDir
}
outputs = outs

View File

@@ -7,6 +7,7 @@ import (
"regexp"
"sort"
"strconv"
"strings"
)
func hasSeq(s string) bool {
@@ -161,3 +162,49 @@ func expandSeq(pat string) ([]string, error) {
return result, nil
}
func hasTemplate(s string) bool {
if !strings.Contains(s, "{") {
return false
}
return strings.Contains(s, "{/.}") ||
strings.Contains(s, "{/}") ||
strings.Contains(s, "{.}") ||
strings.Contains(s, "{}") ||
strings.Contains(s, "{//}") ||
strings.Contains(s, "{#}")
}
func trimExt(p string) string {
return strings.TrimSuffix(p, filepath.Ext(p))
}
func formatTemplate(pattern, in string, idx, startNum int) string {
if hasSeq(pattern) {
pattern = formatSeq(pattern, idx, startNum)
}
dir := filepath.Dir(in)
base := filepath.Base(in)
// Special-case "-" (stdin)
if in == "-" {
dir = "."
base = "stdin"
}
fullNoExt := trimExt(in)
baseNoExt := trimExt(base)
n := idx + startNum - 1
r := strings.NewReplacer(
"{/.}", baseNoExt,
"{//}", dir,
"{/}", base,
"{.}", fullNoExt,
"{}", in,
"{#}", strconv.Itoa(n),
)
return r.Replace(pattern)
}

93
internal/help/help.go Normal file
View File

@@ -0,0 +1,93 @@
package help
import (
"context"
_ "embed"
"fmt"
"io"
"strings"
"github.com/urfave/cli/v3"
)
type HelpTopic struct {
Name string
Description string
Content []byte
}
var (
//go:embed topics/patterns.txt
topicPatterns []byte
topics = []HelpTopic{
{"topics", "List available help topics", nil},
{"patterns", "Input/output path patterns: globs, %d sequences, templates", topicPatterns},
}
)
func Command() *cli.Command {
return &cli.Command{
Name: "help",
Usage: "Show help or a specific topic",
ArgsUsage: "[topic]",
Action: func(ctx context.Context, c *cli.Command) error {
args := c.Args().Slice()
if len(args) == 0 {
parent := c.Root()
if parent == nil {
parent = c
}
return cli.ShowAppHelp(parent)
}
topic := strings.ToLower(strings.TrimSpace(args[0]))
switch topic {
case "topics":
return printTopics(c.Writer)
default:
return printTopic(c.Writer, topic)
}
},
}
}
func printTopics(w io.Writer) error {
var length int
for _, topic := range topics {
length = max(length, len(topic.Name))
}
fmt.Fprintln(w, "USAGE:")
fmt.Fprintln(w, " ffwebp help [topic]")
fmt.Fprintln(w)
fmt.Fprintln(w, "TOPICS:")
for _, topic := range topics {
fmt.Fprintf(w, " %-*s - %s\n", length, topic.Name, topic.Description)
}
return nil
}
func printTopic(w io.Writer, name string) error {
var topic *HelpTopic
for _, tp := range topics {
if tp.Name == name {
topic = &tp
}
}
if topic == nil {
return fmt.Errorf("unknown help topic: %q (see `ffwebp help topics`)", name)
}
_, err := w.Write(topic.Content)
return err
}

View File

@@ -0,0 +1,28 @@
USAGE:
ffwebp [global options]
TOPIC:
Path patterns for input (-i) and output (-o)
INPUT PATTERNS:
- Globs: *, ?, [..] (shell-expanded on Unix; expanded by ffwebp on Windows)
- Numeric sequences: %d, %02d (start at --start-number, default 1)
OUTPUT PATTERNS:
- %d sequences (same numbering as input/index)
- Templates (GNU-parallel style):
{} full input path
{.} input path without last extension
{/} basename (file name)
{/.} basename without extension
{//} directory (no trailing slash)
{#} sequence number (uses --start-number)
EXAMPLES:
ffwebp -i "*.png" -o "{/.}.webp"
ffwebp -i "frames/%03d.png" -o "out/frame-{#}.png" --start-number 10
ffwebp -i "images/**/*.jpg" -o "out/{//}/{/.}.avif"
NOTES:
- Output codec is inferred from the final extension unless --codec is set.
- With multiple inputs, output must be a pattern or directory.