From 4d57d4301100ea9d231563d8d7604408efca034b Mon Sep 17 00:00:00 2001
From: viktorstrate <viktorstrate@gmail.com>
Date: Wed, 13 May 2020 15:28:50 +0200
Subject: [PATCH] Determine image type by file extension

---
 api/scanner/album_scanner.go |  2 +-
 api/scanner/photo_type.go    | 93 ++++++++++++++++++++++++++----------
 api/scanner/process_photo.go |  6 +--
 3 files changed, 73 insertions(+), 28 deletions(-)

diff --git a/api/scanner/album_scanner.go b/api/scanner/album_scanner.go
index 007c1c7..a07cce5 100644
--- a/api/scanner/album_scanner.go
+++ b/api/scanner/album_scanner.go
@@ -19,7 +19,7 @@ import (
 
 type scanner_cache map[string]interface{}
 
-func (cache *scanner_cache) insert_photo_type(path string, content_type FileType) {
+func (cache *scanner_cache) insert_photo_type(path string, content_type ImageType) {
 	(*cache)["photo_type//"+path] = content_type
 }
 
diff --git a/api/scanner/photo_type.go b/api/scanner/photo_type.go
index 5c87b86..53d627f 100644
--- a/api/scanner/photo_type.go
+++ b/api/scanner/photo_type.go
@@ -4,22 +4,25 @@ import (
 	"io"
 	"log"
 	"os"
+	"path/filepath"
+	"strings"
 
 	"github.com/h2non/filetype"
+	"github.com/pkg/errors"
 )
 
-type FileType string
+type ImageType string
 
 const (
-	TypeJpeg FileType = "image/jpeg"
-	TypePng  FileType = "image/png"
-	TypeTiff FileType = "image/tiff"
-	TypeWebp FileType = "image/webp"
-	TypeCr2  FileType = "image/x-canon-cr2"
-	TypeBmp  FileType = "image/bmp"
+	TypeJpeg ImageType = "image/jpeg"
+	TypePng  ImageType = "image/png"
+	TypeTiff ImageType = "image/tiff"
+	TypeWebp ImageType = "image/webp"
+	TypeBmp  ImageType = "image/bmp"
+	TypeCr2  ImageType = "image/x-canon-cr2"
 )
 
-var SupportedMimetypes = [...]FileType{
+var SupportedMimetypes = [...]ImageType{
 	TypeJpeg,
 	TypePng,
 	TypeTiff,
@@ -28,46 +31,88 @@ var SupportedMimetypes = [...]FileType{
 	TypeCr2,
 }
 
-var WebMimetypes = [...]FileType{
+var WebMimetypes = [...]ImageType{
 	TypeJpeg,
 	TypePng,
 	TypeWebp,
 	TypeBmp,
 }
 
-func isPathImage(path string, cache *scanner_cache) bool {
-	if cache.get_photo_type(path) != nil {
-		return true
+var fileExtensions = map[string]ImageType{
+	".jpg":  TypeJpeg,
+	".jpeg": TypeJpeg,
+	".png":  TypePng,
+	".tif":  TypeTiff,
+	".tiff": TypeTiff,
+	".bmp":  TypeBmp,
+	".cr2":  TypeCr2,
+}
+
+func isTypeSupported(img ImageType) bool {
+	for _, supported_mime := range SupportedMimetypes {
+		if supported_mime == img {
+			return true
+		}
 	}
+
+	return false
+}
+
+func getImageType(path string) (*ImageType, error) {
+
+	ext := filepath.Ext(path)
+
+	fileExtType := fileExtensions[strings.ToLower(ext)]
+
+	if isTypeSupported(fileExtType) {
+		return &fileExtType, nil
+	}
+
+	// If extension was not recognized try to read file header
 	file, err := os.Open(path)
 	if err != nil {
-		ScannerError("Could not open file %s: %s\n", path, err)
-		return false
+		return nil, errors.Wrapf(err, "could not open file %s", path)
 	}
 	defer file.Close()
 
 	head := make([]byte, 261)
 	if _, err := file.Read(head); err != nil {
 		if err == io.EOF {
-			return false
+			return nil, nil
 		}
 
-		ScannerError("Could not read file %s: %s\n", path, err)
-		return false
+		return nil, errors.Wrapf(err, "could not read file: %s", path)
 	}
 
-	imgType, err := filetype.Image(head)
+	_imgType, err := filetype.Image(head)
 	if err != nil {
+		return nil, nil
+	}
+
+	imgType := ImageType(_imgType.MIME.Value)
+	if isTypeSupported(imgType) {
+		return &imgType, nil
+	}
+
+	return nil, nil
+}
+
+func isPathImage(path string, cache *scanner_cache) bool {
+	if cache.get_photo_type(path) != nil {
+		return true
+	}
+
+	imageType, err := getImageType(path)
+	if err != nil {
+		ScannerError("%s (%s)", err, path)
 		return false
 	}
 
-	for _, supported_mime := range SupportedMimetypes {
-		if supported_mime == FileType(imgType.MIME.Value) {
-			cache.insert_photo_type(path, supported_mime)
-			return true
-		}
+	if imageType != nil {
+		cache.insert_photo_type(path, *imageType)
+		return true
 	}
 
-	log.Printf("Unsupported image %s of type %s\n", path, imgType.MIME.Value)
+	log.Printf("File is not a supported image %s\n", path)
 	return false
 }
diff --git a/api/scanner/process_photo.go b/api/scanner/process_photo.go
index bf7c975..e51b64f 100644
--- a/api/scanner/process_photo.go
+++ b/api/scanner/process_photo.go
@@ -269,11 +269,11 @@ type ProcessImageData struct {
 	photo           *models.Photo
 	_photoImage     image.Image
 	_thumbnailImage image.Image
-	_contentType    *FileType
+	_contentType    *ImageType
 }
 
 // ContentType reads the image to determine its content type
-func (img *ProcessImageData) ContentType() (*FileType, error) {
+func (img *ProcessImageData) ContentType() (*ImageType, error) {
 	if img._contentType != nil {
 		return img._contentType, nil
 	}
@@ -296,7 +296,7 @@ func (img *ProcessImageData) ContentType() (*FileType, error) {
 		return nil, err
 	}
 
-	imgType := FileType(_imgType.MIME.Value)
+	imgType := ImageType(_imgType.MIME.Value)
 	img._contentType = &imgType
 	return img._contentType, nil
 }
