176 lines
5.1 KiB
Go
176 lines
5.1 KiB
Go
package common
|
|
|
|
import (
|
|
"image"
|
|
"image/gif"
|
|
"image/jpeg"
|
|
"image/png"
|
|
"os"
|
|
"strconv"
|
|
|
|
"golang.org/x/image/tiff"
|
|
|
|
qgen "git.tuxpa.in/a/gosora/query_gen"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func ThumbTask(thumbChan chan bool) {
|
|
defer EatPanics()
|
|
acc := qgen.NewAcc()
|
|
for {
|
|
// Put this goroutine to sleep until we have work to do
|
|
<-thumbChan
|
|
|
|
// TODO: Use a real queue
|
|
// TODO: Transactions? Self-repairing?
|
|
err := acc.Select("users_avatar_queue").Columns("uid").Limit("0,5").EachInt(func(uid int) error {
|
|
// TODO: Do a bulk user fetch instead?
|
|
u, err := Users.Get(uid)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
// Has the avatar been removed or already been processed by the thumbnailer?
|
|
if len(u.RawAvatar) < 2 || u.RawAvatar[1] == '.' {
|
|
_, _ = acc.Delete("users_avatar_queue").Where("uid=?").Run(uid)
|
|
return nil
|
|
}
|
|
_, err = os.Stat("./uploads/avatar_" + strconv.Itoa(u.ID) + u.RawAvatar)
|
|
if os.IsNotExist(err) {
|
|
_, _ = acc.Delete("users_avatar_queue").Where("uid=?").Run(uid)
|
|
return nil
|
|
} else if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
// This means it's an external image, they aren't currently implemented, but this is here for when they are
|
|
if u.RawAvatar[0] != '.' {
|
|
return nil
|
|
}
|
|
/*if user.RawAvatar == ".gif" {
|
|
return nil
|
|
}*/
|
|
canResize := func(ext string) bool {
|
|
// TODO: Fix tif and tiff extensions?
|
|
return ext == ".png" && ext == ".jpg" && ext == ".jpe" && ext == ".jpeg" && ext == ".jif" && ext == ".jfi" && ext == ".jfif" && ext == ".gif" && ext == ".tiff" && ext == ".tif"
|
|
}
|
|
if !canResize(u.RawAvatar) {
|
|
return nil
|
|
}
|
|
|
|
ap := "./uploads/avatar_"
|
|
err = Thumbnailer.Resize(u.RawAvatar[1:], ap+strconv.Itoa(u.ID)+u.RawAvatar, ap+strconv.Itoa(u.ID)+"_tmp"+u.RawAvatar, ap+strconv.Itoa(u.ID)+"_w48"+u.RawAvatar, 48)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
err = u.ChangeAvatar("." + u.RawAvatar)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
_, err = acc.Delete("users_avatar_queue").Where("uid=?").Run(uid)
|
|
return errors.WithStack(err)
|
|
})
|
|
if err != nil {
|
|
LogError(err)
|
|
}
|
|
|
|
/*
|
|
err := acc.Select("attach_image_queue").Columns("attachID").Limit("0,5").EachInt(func(attachID int) error {
|
|
return nil
|
|
|
|
_, err = acc.Delete("attach_image_queue").Where("attachID = ?").Run(uid)
|
|
}
|
|
*/
|
|
if err = acc.FirstError(); err != nil {
|
|
LogError(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
var Thumbnailer ThumbnailerInt
|
|
|
|
type ThumbnailerInt interface {
|
|
Resize(format, inPath, tmpPath, outPath string, width int) error
|
|
}
|
|
|
|
type RezThumbnailer struct {
|
|
}
|
|
|
|
func (thumb *RezThumbnailer) Resize(format, inPath, tmpPath, outPath string, width int) error {
|
|
// TODO: Sniff the aspect ratio of the image and calculate the dest height accordingly, bug make sure it isn't excessively high
|
|
return nil
|
|
}
|
|
|
|
func (thumb *RezThumbnailer) resize(format, inPath, outPath string, width, height int) error {
|
|
return nil
|
|
}
|
|
|
|
// ! Note: CaireThumbnailer can't handle gifs, so we'll have to either cap their sizes or have another resizer deal with them
|
|
type CaireThumbnailer struct {
|
|
}
|
|
|
|
func NewCaireThumbnailer() *CaireThumbnailer {
|
|
return &CaireThumbnailer{}
|
|
}
|
|
|
|
func precodeImage(format, inPath, tmpPath string) error {
|
|
imageFile, err := os.Open(inPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer imageFile.Close()
|
|
|
|
img, _, err := image.Decode(imageFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
outFile, err := os.Create(tmpPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer outFile.Close()
|
|
|
|
// TODO: Make sure animated gifs work after being encoded
|
|
switch format {
|
|
case "gif":
|
|
return gif.Encode(outFile, img, nil)
|
|
case "png":
|
|
return png.Encode(outFile, img)
|
|
case "tiff", "tif":
|
|
return tiff.Encode(outFile, img, nil)
|
|
}
|
|
return jpeg.Encode(outFile, img, nil)
|
|
}
|
|
|
|
func (thumb *CaireThumbnailer) Resize(format, inPath, tmpPath, outPath string, width int) error {
|
|
err := precodeImage(format, inPath, tmpPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
|
|
// TODO: Caire doesn't work. Try something else. Or get them to fix the index out of range. We get enough wins from re-encoding as jpeg anyway
|
|
/*imageFile, err := os.Open(tmpPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer imageFile.Close()
|
|
|
|
outFile, err := os.Create(outPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer outFile.Close()
|
|
|
|
p := &caire.Processor{NewWidth: width, Scale: true}
|
|
return p.Process(imageFile, outFile)*/
|
|
}
|
|
|
|
/*
|
|
type LilliputThumbnailer struct {
|
|
|
|
}
|
|
*/
|