beginning media tools

pull/8/head
Stephen Searles 8 years ago
parent 4a5a20e49e
commit ffd14c5795
  1. 27
      caddyhugo.go
  2. 214
      media.go

@ -52,6 +52,8 @@ type CaddyHugo struct {
ServerType string ServerType string
Site *httpserver.SiteConfig Site *httpserver.SiteConfig
Media *MediaSource
docs map[string]*docref docs map[string]*docref
mtx sync.Mutex mtx sync.Mutex
@ -100,6 +102,21 @@ func (ch *CaddyHugo) Setup(c *caddy.Controller) error {
return fmt.Errorf("edit template invalid: %v", err) return fmt.Errorf("edit template invalid: %v", err)
} }
thumbDir, err := ioutil.TempDir("", "thumbs")
if err != nil {
return fmt.Errorf("couldn't initialize media: %v", err)
}
ch.Media = &MediaSource{
StorageDir: path.Join(ch.Site.Root, "media"),
ThumbDir: thumbDir,
}
err = os.MkdirAll(ch.Media.StorageDir, 0755)
if err != nil {
return fmt.Errorf("couldn't initialize media: %v", err)
}
// add a function that wraps listeners for the HTTP server // add a function that wraps listeners for the HTTP server
// (it's more common for a directive to call this rather than a standalone plugin) // (it's more common for a directive to call this rather than a standalone plugin)
ch.Site.AddMiddleware(ch.Middleware(c)) ch.Site.AddMiddleware(ch.Middleware(c))
@ -148,6 +165,12 @@ func (ch *CaddyHugo) ServeHTTPWithNext(next httpserver.Handler, c *caddy.Control
if strings.HasPrefix(r.URL.Path, "/hugo/draft/") { if strings.HasPrefix(r.URL.Path, "/hugo/draft/") {
return ch.serveDraft(w, r) return ch.serveDraft(w, r)
} }
if strings.HasPrefix(r.URL.Path, "/hugo/media") {
return ch.serveMediaPage(w, r)
}
if strings.HasPrefix(r.URL.Path, "/media/") {
return ch.serveMedia(w, r)
}
http.NotFound(w, r) http.NotFound(w, r)
return 404, nil return 404, nil
@ -166,6 +189,10 @@ func (ch CaddyHugo) Auth(r *http.Request) bool {
} }
func (ch CaddyHugo) Match(r *http.Request) bool { func (ch CaddyHugo) Match(r *http.Request) bool {
if strings.HasPrefix(r.URL.Path, "/media/") {
return true
}
if r.URL.Path == "/hugo" { if r.URL.Path == "/hugo" {
return true return true
} }

@ -0,0 +1,214 @@
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, 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
}
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
}
return ms.ThumbImage(img, m, width, height)
}
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; }</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, err := ch.Media.ThumbMax(*m, 100)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return 500, nil
}
fmt.Fprintf(w, "<img src=%q />\n", 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)]
}
Loading…
Cancel
Save