You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
caddy-hugo2/media.go

216 lines
4.7 KiB

package caddyhugo
import (
"fmt"
"image"
"image/jpeg"
"io"
"net/http"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"github.com/nfnt/resize"
)
type MediaSource struct {
StorageDir string
ThumbDir string
}
func (ms *MediaSource) LocationOrig(m Media) string {
return path.Join(ms.StorageDir, m.Name)
}
type Media struct {
Type string
Name string
}
func (ms *MediaSource) ThumbMax(m Media, maxDim int) (string, int, int, error) {
f, err := os.Open(ms.LocationOrig(m))
if err != nil {
return "", 0, 0, err
}
defer f.Close()
img, _, err := image.Decode(f)
if err != nil {
return "", 0, 0, err
}
rect := img.Bounds()
width := rect.Dx()
height := rect.Dy()
if width > height {
height = height * maxDim / width
width = maxDim
} else {
width = width * maxDim / height
height = maxDim
}
src, err := ms.ThumbImage(img, m, width, height)
return src, width, height, err
}
func (ms *MediaSource) ByName(name string) *Media {
return &Media{
Type: "image",
Name: name,
}
}
func (ms *MediaSource) Thumb(m Media, width, height int) (string, error) {
f, err := os.Open(ms.LocationOrig(m))
if err != nil {
return "", err
}
defer f.Close()
img, _, err := image.Decode(f)
if err != nil {
return "", err
}
return ms.ThumbImage(img, m, width, height)
}
func (ms *MediaSource) ThumbImage(img image.Image, m Media, width, height int) (string, error) {
thumbSlug := filepath.Join(m.Name, fmt.Sprintf("%d/%d.jpg", width, height))
thumbLoc := filepath.Join(ms.ThumbDir, thumbSlug)
os.MkdirAll(path.Dir(thumbLoc), 0755)
fthumb, err := os.OpenFile(thumbLoc, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0655)
if err != nil {
return "", err
}
img = resize.Resize(uint(width), uint(height), img, resize.Bilinear)
err = jpeg.Encode(fthumb, img, nil)
if err != nil {
return "", err
}
err = fthumb.Close()
if err != nil {
return "", err
}
return path.Join("/media", thumbSlug), nil
}
func (ms *MediaSource) Walk() ([]*Media, error) {
var media []*Media
err := filepath.Walk(ms.StorageDir, func(name string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
if fi.IsDir() {
return nil
}
media = append(media, ms.ByName(path.Base(name)))
return nil
})
return media, err
}
func (ch *CaddyHugo) serveMediaPage(w http.ResponseWriter, r *http.Request) (int, error) {
if ch.Media == nil {
http.NotFound(w, r)
return 404, nil
}
io.WriteString(w, `<html>
<head><style>.img { float: left; text-align: center; }</style></head>
<body>
`)
if ch.Media != nil {
media, err := ch.Media.Walk()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return 500, nil
}
for _, m := range media {
src, width, height, err := ch.Media.ThumbMax(*m, 100)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return 500, nil
}
fmt.Fprintf(w, `<div class="img"><img width=%d height=%d src=%q /><br /><input type="text" disabled value=%q /></div>`, width, height, src, src)
}
}
io.WriteString(w, `</body><html>`)
return 200, nil
}
func (ch *CaddyHugo) serveMedia(w http.ResponseWriter, r *http.Request) (int, error) {
if ch.Media == nil {
http.NotFound(w, r)
return 404, nil
}
segs := strings.Split(r.URL.Path, "/")
name := segs[2]
file := ""
var err error
m := ch.Media.ByName(name)
switch len(segs) {
case 3:
file = name
case 4:
var max int
max, err = strconv.Atoi(removeExtension(segs[3]))
if err != nil {
http.Error(w, fmt.Sprintf("expected /media/filename, /media/filename/maxDim, or /media/filename/width/height..."), http.StatusBadRequest)
return 400, nil
}
file, _, _, err = ch.Media.ThumbMax(*m, max)
case 5:
var width, height int
width, err = strconv.Atoi(segs[3])
if err != nil {
http.Error(w, fmt.Sprintf("expected /media/filename, /media/filename/maxDim, or /media/filename/width/height.."), http.StatusBadRequest)
return 400, nil
}
height, err = strconv.Atoi(removeExtension(segs[4]))
if err != nil {
http.Error(w, fmt.Sprintf("expected /media/filename, /media/filename/maxDim, or /media/filename/width/height."), http.StatusBadRequest)
return 400, nil
}
file, err = ch.Media.Thumb(*m, width, height)
default:
http.Error(w, fmt.Sprintf("expected /media/filename, /media/filename/maxDim, or /media/filename/width/height"), http.StatusBadRequest)
return 400, nil
}
if err != nil {
http.Error(w, fmt.Sprintf("unable to load thumb"), http.StatusInternalServerError)
return 500, nil
}
if file[0] == '/' {
file = file[1:]
}
file = path.Join(ch.Media.ThumbDir, file[len("media/"):])
http.ServeFile(w, r, file)
return 200, nil
}
func removeExtension(name string) string {
ext := path.Ext(name)
return name[:len(name)-len(ext)]
}