1
Fork 0

Work on image processing

This commit is contained in:
viktorstrate 2020-02-02 18:18:38 +01:00
parent d81e945b68
commit 11896a246e
5 changed files with 119 additions and 10 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
cache/
image-cache/
/photos_path
.env

View File

@ -10,6 +10,7 @@ require (
github.com/h2non/filetype v1.0.10
github.com/joho/godotenv v1.3.0
github.com/lib/pq v1.3.0
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/vektah/gqlparser v1.2.0
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
)

View File

@ -28,6 +28,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=

View File

@ -2,14 +2,110 @@ package scanner
import (
"database/sql"
"image"
"image/jpeg"
"log"
"math/rand"
"os"
"path"
"strconv"
"github.com/nfnt/resize"
)
func ProcessImage(tx *sql.Tx, photoPath string, albumId int) error {
log.Printf("Processing image: %s\n", photoPath)
photoName := path.Base(photoPath)
_, err := tx.Exec("INSERT IGNORE INTO photo (title, path, album_id) VALUES (?, ?, ?)", photoName, photoPath, albumId)
// Check if image already exists
row := tx.QueryRow("SELECT (photo_id) FROM photo WHERE path = ?", photoPath)
var id int
if err := row.Scan(&id); err != sql.ErrNoRows {
if err == nil {
log.Printf("Image already processed: %s\n", photoPath)
return nil
} else {
return err
}
}
thumbFile, err := os.Open(photoPath)
if err != nil {
return err
}
defer thumbFile.Close()
image, _, err := image.Decode(thumbFile)
if err != nil {
log.Println("ERROR: decoding image")
return err
}
thumbnailImage := resize.Thumbnail(1024, 1024, image, resize.Bilinear)
if _, err := os.Stat("image-cache"); os.IsNotExist(err) {
if err := os.Mkdir("image-cache", os.ModePerm); err != nil {
log.Println("ERROR: Could not make image cache directory")
return err
}
}
// Make album cache dir
albumCachePath := path.Join("image-cache", strconv.Itoa(albumId))
if _, err := os.Stat(albumCachePath); os.IsNotExist(err) {
if err := os.Mkdir(albumCachePath, os.ModePerm); err != nil {
log.Println("ERROR: Could not make album image cache directory")
return err
}
}
// Generate image token name
thumbnailToken := generateToken()
originalToken := generateToken()
// Save thumbnail as jpg
thumbFile, err = os.Create(path.Join(albumCachePath, thumbnailToken+".jpg"))
if err != nil {
log.Println("ERROR: Could not make thumbnail file")
return err
}
defer thumbFile.Close()
jpeg.Encode(thumbFile, thumbnailImage, &jpeg.Options{Quality: 70})
thumbSize := thumbnailImage.Bounds().Max
thumbRes, err := tx.Exec("INSERT INTO photo_url (token, width, height) VALUES (?, ?, ?)", thumbnailToken, thumbSize.X, thumbSize.Y)
if err != nil {
return err
}
thumbUrlId, err := thumbRes.LastInsertId()
if err != nil {
return err
}
origSize := image.Bounds().Max
origRes, err := tx.Exec("INSERT INTO photo_url (token, width, height) VALUES (?, ?, ?)", originalToken, origSize.X, origSize.Y)
if err != nil {
return err
}
origUrlId, err := origRes.LastInsertId()
_, err = tx.Exec("INSERT INTO photo (title, path, album_id, original_url, thumbnail_url) VALUES (?, ?, ?, ?, ?)", photoName, photoPath, albumId, origUrlId, thumbUrlId)
if err != nil {
log.Printf("ERROR: Could not insert photo into database")
return err
}
return nil
}
func generateToken() string {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
const length = 24
b := make([]byte, length)
for i := range b {
b[i] = charset[rand.Intn(len(charset))]
}
return string(b)
}

View File

@ -79,23 +79,31 @@ func scan(database *sql.DB, user *models.User) {
return
}
// Commit album transaction
if err := tx.Commit(); err != nil {
log.Printf("ERROR: Could not commit database transaction: %s\n", err)
return
}
// Scan for photos
for _, item := range dirContent {
photoPath := path.Join(albumPath, item.Name())
if !item.IsDir() && isPathImage(photoPath) {
tx, err := database.Begin()
if err != nil {
log.Printf("ERROR: Could not begin database transaction for image %s: %s\n", photoPath, err)
return
}
if err := ProcessImage(tx, photoPath, albumId); err != nil {
fmt.Printf("ERROR: Could not proccess image %s: %s", photoPath, err)
log.Printf("ERROR: processing image %s: %s", photoPath, err)
tx.Rollback()
return
}
}
}
// Commit album and photos transaction
if err := tx.Commit(); err != nil {
log.Printf("ERROR: Could not commit database transaction: %s\n", err)
return
tx.Commit()
}
}
// Scan for sub-albums
@ -148,8 +156,8 @@ func directoryContainsPhotos(rootPath string) bool {
var supported_mimetypes = [...]string{
"image/jpeg",
"image/png",
"image/tiff",
"image/x-canon-cr2",
// "image/tiff",
// "image/x-canon-cr2",
"image/bmp",
}
@ -159,6 +167,7 @@ func isPathImage(path string) bool {
log.Printf("Could not open file %s: %s\n", path, err)
return false
}
defer file.Close()
head := make([]byte, 261)
if _, err := file.Read(head); err != nil {