Work on image processing
This commit is contained in:
parent
d81e945b68
commit
11896a246e
|
@ -1,6 +1,7 @@
|
||||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
|
|
||||||
cache/
|
cache/
|
||||||
|
image-cache/
|
||||||
/photos_path
|
/photos_path
|
||||||
|
|
||||||
.env
|
.env
|
||||||
|
|
|
@ -10,6 +10,7 @@ require (
|
||||||
github.com/h2non/filetype v1.0.10
|
github.com/h2non/filetype v1.0.10
|
||||||
github.com/joho/godotenv v1.3.0
|
github.com/joho/godotenv v1.3.0
|
||||||
github.com/lib/pq 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
|
github.com/vektah/gqlparser v1.2.0
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
||||||
)
|
)
|
||||||
|
|
|
@ -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 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
|
||||||
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
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/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/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
|
|
|
@ -2,14 +2,110 @@ package scanner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"image"
|
||||||
|
"image/jpeg"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/nfnt/resize"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ProcessImage(tx *sql.Tx, photoPath string, albumId int) error {
|
func ProcessImage(tx *sql.Tx, photoPath string, albumId int) error {
|
||||||
|
|
||||||
|
log.Printf("Processing image: %s\n", photoPath)
|
||||||
|
|
||||||
photoName := path.Base(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 {
|
if err != nil {
|
||||||
return err
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,23 +79,31 @@ func scan(database *sql.DB, user *models.User) {
|
||||||
return
|
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
|
// Scan for photos
|
||||||
for _, item := range dirContent {
|
for _, item := range dirContent {
|
||||||
photoPath := path.Join(albumPath, item.Name())
|
photoPath := path.Join(albumPath, item.Name())
|
||||||
|
|
||||||
if !item.IsDir() && isPathImage(photoPath) {
|
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 {
|
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()
|
tx.Rollback()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit album and photos transaction
|
tx.Commit()
|
||||||
if err := tx.Commit(); err != nil {
|
}
|
||||||
log.Printf("ERROR: Could not commit database transaction: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan for sub-albums
|
// Scan for sub-albums
|
||||||
|
@ -148,8 +156,8 @@ func directoryContainsPhotos(rootPath string) bool {
|
||||||
var supported_mimetypes = [...]string{
|
var supported_mimetypes = [...]string{
|
||||||
"image/jpeg",
|
"image/jpeg",
|
||||||
"image/png",
|
"image/png",
|
||||||
"image/tiff",
|
// "image/tiff",
|
||||||
"image/x-canon-cr2",
|
// "image/x-canon-cr2",
|
||||||
"image/bmp",
|
"image/bmp",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,6 +167,7 @@ func isPathImage(path string) bool {
|
||||||
log.Printf("Could not open file %s: %s\n", path, err)
|
log.Printf("Could not open file %s: %s\n", path, err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
head := make([]byte, 261)
|
head := make([]byte, 261)
|
||||||
if _, err := file.Read(head); err != nil {
|
if _, err := file.Read(head); err != nil {
|
||||||
|
|
Loading…
Reference in New Issue