Created
February 5, 2025 11:19
-
-
Save peterhellberg/e88b216e045e6e8854d3e4e2821942f0 to your computer and use it in GitHub Desktop.
Small script that downloads images I publish on Imgur to my own server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"errors" | |
"flag" | |
"fmt" | |
"io" | |
"net/http" | |
"os" | |
"path/filepath" | |
"strings" | |
"time" | |
) | |
var ( | |
defaultDir = "/var/www/assets.c7.se/imgur" | |
defaultURL = "https://assets.c7.se/imgur/" | |
defaultRaw = false | |
) | |
const ( | |
iImgurCom = `https://i.imgur.com/` | |
firefoxUA = `Mozilla/5.0 (X11; Linux i686; rv:135.0) Gecko/20100101 Firefox/135.0` | |
) | |
type config struct { | |
dir string | |
url string | |
src string | |
img string | |
raw bool | |
} | |
func (cfg config) path() string { | |
return filepath.Join(cfg.dir, cfg.img) | |
} | |
func (cfg config) out() string { | |
return cfg.url + cfg.img | |
} | |
func main() { | |
if err := run(os.Args, os.Stdout); err != nil { | |
fmt.Fprintf(os.Stderr, "❌ %s\n", err) | |
os.Exit(1) | |
} | |
} | |
func run(args []string, w io.Writer) error { | |
cfg, err := parse(args) | |
if err != nil { | |
return err | |
} | |
client := &http.Client{ | |
Timeout: time.Minute, | |
} | |
if err := download(cfg, client); err != nil { | |
return err | |
} | |
return output(w, cfg) | |
} | |
func request(cfg config) (*http.Request, error) { | |
req, err := http.NewRequest(http.MethodGet, cfg.src, nil) | |
if err != nil { | |
return nil, err | |
} | |
req.Header.Set("User-Agent", firefoxUA) | |
return req, nil | |
} | |
func download(cfg config, client *http.Client) error { | |
name := cfg.path() | |
if _, err := os.Stat(name); !errors.Is(err, os.ErrNotExist) { | |
return fmt.Errorf("file already exists, served on %s", cfg.out()) | |
} | |
req, err := request(cfg) | |
if err != nil { | |
return err | |
} | |
resp, err := client.Do(req) | |
if err != nil { | |
return err | |
} | |
defer resp.Body.Close() | |
if resp.StatusCode != http.StatusOK { | |
return fmt.Errorf("bad status: %s", resp.Status) | |
} | |
dst, err := os.Create(name) | |
if err != nil { | |
return err | |
} | |
defer dst.Close() | |
_, err = io.Copy(dst, resp.Body) | |
return err | |
} | |
func output(w io.Writer, cfg config) error { | |
if cfg.raw { | |
return fprintf(w, "%s\n", cfg.out()) | |
} | |
return fprintf(w, "✅ file downloaded, served on %s\n", cfg.out()) | |
} | |
func fprintf(w io.Writer, format string, a ...any) error { | |
_, err := fmt.Fprintf(w, format, a...) | |
return err | |
} | |
func parse(args []string) (config, error) { | |
var cfg config | |
flags := flag.NewFlagSet(args[0], flag.ExitOnError) | |
flags.StringVar(&cfg.dir, "dir", defaultDir, "Local directory to download to") | |
flags.StringVar(&cfg.url, "url", defaultURL, "URL to construct links from") | |
flags.BoolVar(&cfg.raw, "raw", defaultRaw, "If to output just the link URL") | |
if err := flags.Parse(args[1:]); err != nil { | |
return cfg, err | |
} | |
cfg.src = strings.Join(flags.Args(), "") | |
switch { | |
case cfg.src == "": | |
return cfg, fmt.Errorf("no URL provided") | |
case !strings.HasPrefix(cfg.src, iImgurCom): | |
return cfg, fmt.Errorf("provided source %q does not have the required prefix %q", cfg.src, iImgurCom) | |
} | |
cfg.img = filepath.Base(cfg.src) | |
return cfg, nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
On my workstation I have a small script that rely on SSHFS to mount my server to a local directory: