2016-11-18 06:44:27 +00:00
|
|
|
package thumbnail
|
|
|
|
|
|
|
|
import (
|
2016-12-05 08:48:48 +00:00
|
|
|
"errors"
|
2016-12-05 06:08:13 +00:00
|
|
|
"image/gif"
|
2016-11-18 06:44:27 +00:00
|
|
|
"image/jpeg"
|
|
|
|
"image/png"
|
|
|
|
"os"
|
2016-12-05 06:03:05 +00:00
|
|
|
"path/filepath"
|
2016-11-18 06:44:27 +00:00
|
|
|
"strings"
|
2016-11-18 07:02:28 +00:00
|
|
|
|
|
|
|
lru "github.com/hashicorp/golang-lru"
|
2016-11-18 06:44:27 +00:00
|
|
|
)
|
|
|
|
|
2016-12-05 08:48:48 +00:00
|
|
|
type OutputFormat uint8
|
|
|
|
type AspectFormat uint8
|
|
|
|
type ScaleFormat uint8
|
|
|
|
|
|
|
|
const (
|
|
|
|
OUTPUT_PNG_CRUSH OutputFormat = 2
|
|
|
|
OUTPUT_JPG OutputFormat = 3
|
|
|
|
OUTPUT__DEFAULT = OUTPUT_PNG_CRUSH
|
|
|
|
|
|
|
|
ASPECT_PADDED AspectFormat = 80
|
|
|
|
ASPECT_SVELTE AspectFormat = 81
|
|
|
|
ASPECT__DEFAULT = ASPECT_PADDED
|
|
|
|
|
|
|
|
SCALEFMT_NN ScaleFormat = 120
|
|
|
|
SCALEFMT_BILINEAR ScaleFormat = 121
|
|
|
|
SCALEFMT__DEFAULT = SCALEFMT_BILINEAR
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
ErrInvalidOption error = errors.New("Invalid format parameter")
|
|
|
|
ErrUnsupportedFiletype error = errors.New("Unsupported filetype")
|
|
|
|
)
|
|
|
|
|
2016-11-18 06:44:27 +00:00
|
|
|
type Thumbnailer struct {
|
2016-12-05 08:48:48 +00:00
|
|
|
width int
|
|
|
|
height int
|
|
|
|
ofmt OutputFormat
|
|
|
|
afmt AspectFormat
|
|
|
|
sfmt ScaleFormat
|
2016-11-18 06:44:27 +00:00
|
|
|
|
2016-11-18 07:02:28 +00:00
|
|
|
thumbCache *lru.Cache // threadsafe
|
2016-11-18 06:44:27 +00:00
|
|
|
}
|
|
|
|
|
2016-11-18 07:02:28 +00:00
|
|
|
func NewThumbnailer(Width, Height, MaxCacheSize int) *Thumbnailer {
|
2016-12-05 08:48:48 +00:00
|
|
|
return NewThumbnailerEx(Width, Height, MaxCacheSize, OUTPUT__DEFAULT, ASPECT__DEFAULT, SCALEFMT__DEFAULT)
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewThumbnailerEx(Width, Height, MaxCacheSize int, of OutputFormat, af AspectFormat, sf ScaleFormat) *Thumbnailer {
|
2016-11-18 07:02:28 +00:00
|
|
|
thumbCache, err := lru.New(MaxCacheSize)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2016-11-18 06:44:27 +00:00
|
|
|
return &Thumbnailer{
|
2016-12-05 08:48:48 +00:00
|
|
|
width: Width,
|
|
|
|
height: Height,
|
|
|
|
ofmt: of,
|
|
|
|
afmt: af,
|
|
|
|
sfmt: sf,
|
2016-11-18 07:02:28 +00:00
|
|
|
thumbCache: thumbCache,
|
2016-11-18 06:44:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *Thumbnailer) RenderFile(absPath string) ([]byte, error) {
|
|
|
|
|
2016-11-18 07:02:28 +00:00
|
|
|
thumb, ok := this.thumbCache.Get(absPath)
|
2016-11-18 06:44:27 +00:00
|
|
|
if ok {
|
2016-11-18 07:02:28 +00:00
|
|
|
return thumb.([]byte), nil
|
2016-11-18 06:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add to cache
|
2016-11-18 07:02:28 +00:00
|
|
|
thumb, err := this.RenderFile_NoCache(absPath)
|
2016-11-18 06:44:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-11-18 07:02:28 +00:00
|
|
|
this.thumbCache.Add(absPath, thumb)
|
|
|
|
return thumb.([]byte), nil
|
2016-11-18 06:44:27 +00:00
|
|
|
}
|
|
|
|
|
2016-12-05 06:47:24 +00:00
|
|
|
func FiletypeSupported(ext string) bool {
|
|
|
|
switch strings.ToLower(ext) {
|
2016-12-05 06:48:04 +00:00
|
|
|
case ".jpg", ".jpeg", ".png", ".gif", ".avi", ".mkv", ".mp4", ".ogm", ".wmv", ".flv", ".rm", ".rmvb":
|
2016-12-05 06:47:24 +00:00
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-18 06:44:27 +00:00
|
|
|
func (this *Thumbnailer) RenderFile_NoCache(absPath string) ([]byte, error) {
|
|
|
|
|
|
|
|
fh, err := os.OpenFile(absPath, os.O_RDONLY, 0400)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer fh.Close()
|
|
|
|
|
2016-12-05 06:03:05 +00:00
|
|
|
extension := strings.ToLower(filepath.Ext(absPath))
|
2016-11-18 06:44:27 +00:00
|
|
|
|
2016-11-18 07:02:22 +00:00
|
|
|
switch extension {
|
2016-12-05 06:05:35 +00:00
|
|
|
case ".jpg", ".jpeg":
|
2016-11-18 07:02:22 +00:00
|
|
|
src, err := jpeg.Decode(fh)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-11-18 06:44:27 +00:00
|
|
|
|
2016-11-18 07:02:22 +00:00
|
|
|
return this.RenderScaledImage(src)
|
2016-11-18 06:44:27 +00:00
|
|
|
|
2016-12-05 06:05:35 +00:00
|
|
|
case ".png":
|
2016-11-18 07:02:22 +00:00
|
|
|
src, err := png.Decode(fh)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-11-18 06:44:27 +00:00
|
|
|
|
2016-11-18 07:02:22 +00:00
|
|
|
return this.RenderScaledImage(src)
|
|
|
|
|
2016-12-05 06:08:13 +00:00
|
|
|
case ".gif":
|
|
|
|
src, err := gif.Decode(fh)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.RenderScaledImage(src)
|
|
|
|
|
2016-12-05 06:48:04 +00:00
|
|
|
case ".avi", ".mkv", ".mp4", ".ogm", ".wmv", ".flv", ".rm", ".rmvb":
|
2016-11-18 07:02:22 +00:00
|
|
|
return this.RenderScaledFfmpeg(absPath)
|
|
|
|
|
|
|
|
default:
|
2016-12-05 08:48:48 +00:00
|
|
|
return nil, ErrUnsupportedFiletype
|
2016-11-18 06:44:27 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|