cmd/mdget.go (view raw)
1package main
2
3import (
4 "fmt"
5 "os"
6 "strings"
7 "time"
8
9 htmltomarkdown "github.com/JohannesKaufmann/html-to-markdown/v2"
10 "github.com/JohannesKaufmann/html-to-markdown/v2/converter"
11 "github.com/alexflint/go-arg"
12 "github.com/imroc/req/v3"
13)
14
15var (
16 version = "0.1.0"
17 buildDate = "2026-04-20"
18)
19
20// Args defines the command-line arguments for mdget
21type Args struct {
22 URL string `arg:"positional" help:"URL to fetch and convert to markdown"`
23 Output string `arg:"-o,--output" help:"Output file (default: stdout)" default:""`
24 Timeout int `arg:"-t,--timeout" help:"Request timeout in seconds" default:"30"`
25 UserAgent string `arg:"-u,--user-agent" help:"Custom User-Agent header" default:"mdget/0.1.0"`
26}
27
28// Version implements the Versioned interface for go-arg
29func (Args) Version() string {
30 return fmt.Sprintf("mdget v%s (built %s)", version, buildDate)
31}
32
33// Description returns the program description for help text
34func (Args) Description() string {
35 return "Fetch a URL and convert its HTML content to Markdown."
36}
37
38// Epilogue returns the help text epilogue
39func (Args) Epilogue() string {
40 return `Examples:
41 mdget https://example.com
42 mdget --output page.md https://example.com
43 mdget --timeout 10 --user-agent "MyBot/1.0" example.com`
44}
45
46func main() {
47 // Parse command-line arguments
48 var args Args
49
50 // Use MustParse which handles --help and --version automatically
51 parser := arg.MustParse(&args)
52
53 // Check if URL is provided (help/version flags are already handled by MustParse)
54 if args.URL == "" {
55 fmt.Fprintf(os.Stderr, "Error: URL is required\n\n")
56 parser.WriteUsage(os.Stderr)
57 os.Exit(1)
58 }
59
60 // Validate URL starts with http:// or https://
61 url := args.URL
62 if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
63 url = "https://" + url
64 }
65
66 // Determine output destination
67 var output *os.File = os.Stdout
68 if args.Output != "" {
69 f, err := os.Create(args.Output)
70 if err != nil {
71 fmt.Fprintf(os.Stderr, "Error creating output file: %v\n", err)
72 os.Exit(1)
73 }
74 defer f.Close()
75 output = f
76 }
77
78 // Fetch the URL using req with timeout and custom User-Agent
79 client := req.C().
80 SetTimeout(time.Duration(args.Timeout) * time.Second).
81 SetUserAgent(args.UserAgent)
82
83 resp, err := client.R().Get(url)
84 if err != nil {
85 fmt.Fprintf(os.Stderr, "Error fetching URL: %v\n", err)
86 os.Exit(1)
87 }
88
89 if !resp.IsSuccessState() {
90 fmt.Fprintf(os.Stderr, "HTTP error: %s\n", resp.Status)
91 os.Exit(1)
92 }
93
94 // Get HTML content
95 html := resp.String()
96
97 // Convert HTML to Markdown with domain context for relative URLs
98 markdown, err := htmltomarkdown.ConvertString(
99 html,
100 converter.WithDomain(url),
101 )
102 if err != nil {
103 fmt.Fprintf(os.Stderr, "Error converting HTML to Markdown: %v\n", err)
104 os.Exit(1)
105 }
106
107 // Output Markdown to destination
108 fmt.Fprint(output, markdown)
109}
110