diff --git a/Thumbnailer.go b/Thumbnailer.go new file mode 100644 index 0000000..d649a3b --- /dev/null +++ b/Thumbnailer.go @@ -0,0 +1,81 @@ +package thumbnail + +import ( + "fmt" + "image" + "image/jpeg" + "image/png" + "os" + "strings" + "sync" +) + +type Thumbnailer struct { + Width int + Height int + MaxCacheSize int + + cacheMtx sync.RWMutex + thumbCache map[string][]byte +} + +func NewThumbnailer() *Thumbnailer { + return &Thumbnailer{ + Width: 100, + Height: 100, + MaxCacheSize: 10, + thumbCache: make(map[string][]byte, 0), + } +} + +func (this *Thumbnailer) RenderFile(absPath string) ([]byte, error) { + this.cacheMtx.RLock() + tcache, ok := this.thumbCache[absPath] + this.cacheMtx.RUnlock() + + if ok { + return tcache, nil + } + + // Add to cache + tcache, err := this.RenderFile_NoCache(absPath) + if err != nil { + return nil, err + } + + this.cacheMtx.Lock() + // FIXME prune old cached thumbnails + this.thumbCache[absPath] = tcache + this.cacheMtx.Unlock() + + return tcache, nil +} + +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() + + var src image.Image + err = fmt.Errorf("No thumbnailer for file type") + + comparePath := strings.ToLower(absPath) + + if strings.HasSuffix(comparePath, "jpg") { + src, err = jpeg.Decode(fh) + + } else if strings.HasSuffix(comparePath, "png") { + src, err = png.Decode(fh) + + } + + if err != nil { + return nil, err + } + + return this.RenderScaledImage(src) +} diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..9a259ce --- /dev/null +++ b/doc.go @@ -0,0 +1,3 @@ +// Package thumbnail contains functions for taking the thumbnail of image and +// video files, and caching the result for performance. +package thumbnail diff --git a/thumbnail.go b/image.go similarity index 54% rename from thumbnail.go rename to image.go index cfcd598..ec0acf3 100644 --- a/thumbnail.go +++ b/image.go @@ -1,106 +1,12 @@ -// Package thumbnail contains functions for taking the thumbnail of image and -// video files, and caching the result for performance. package thumbnail import ( "bytes" - "fmt" "image" "image/color" - "image/jpeg" "image/png" - "os" - "sync" - //"os/exec" - "strings" ) -type Thumbnailer struct { - Width int - Height int - MaxCacheSize int - - cacheMtx sync.RWMutex - thumbCache map[string][]byte -} - -func NewThumbnailer() *Thumbnailer { - return &Thumbnailer{ - Width: 100, - Height: 100, - MaxCacheSize: 10, - thumbCache: make(map[string][]byte, 0), - } -} - -func videoThumbnail(absPath string) { - /* - cmd := exec.Command( - "ffmpeg", - "-loglevel", "0", - "-an", - "-i", absPath, - "-vf", "thumbnail,scale=100:100", - "-frames:v", "1", - "-f", "image2pipe", - "-c:v", "png", - "-", - ) - */ -} - -func (this *Thumbnailer) RenderFile(absPath string) ([]byte, error) { - this.cacheMtx.RLock() - tcache, ok := this.thumbCache[absPath] - this.cacheMtx.RUnlock() - - if ok { - return tcache, nil - } - - // Add to cache - tcache, err := this.RenderFile_NoCache(absPath) - if err != nil { - return nil, err - } - - this.cacheMtx.Lock() - // FIXME prune old cached thumbnails - this.thumbCache[absPath] = tcache - this.cacheMtx.Unlock() - - return tcache, nil -} - -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() - - var src image.Image - err = fmt.Errorf("No thumbnailer for file type") - - comparePath := strings.ToLower(absPath) - - if strings.HasSuffix(comparePath, "jpg") { - src, err = jpeg.Decode(fh) - - } else if strings.HasSuffix(comparePath, "png") { - src, err = png.Decode(fh) - - } - - if err != nil { - return nil, err - } - - return this.RenderScaledImage(src) -} - func Blend(a, b color.Color) color.Color { switch a.(type) { case color.RGBA: diff --git a/video.go b/video.go new file mode 100644 index 0000000..e75b457 --- /dev/null +++ b/video.go @@ -0,0 +1,22 @@ +package thumbnail + +import ( + "os/exec" +) + +func (this *Thumbnailer) RenderScaledFfmpeg(absPath string) ([]byte, error) { + + cmd := exec.Command( + "ffmpeg", + "-loglevel", "0", + "-an", + "-i", absPath, + "-vf", "thumbnail,scale=100:100", + "-frames:v", "1", + "-f", "image2pipe", + "-c:v", "png", + "-", + ) + + cmd.StdoutPipe() +}