|
@@ -1,106 +1,189 @@
|
|
|
-package metadata
|
|
|
-
|
|
|
-import (
|
|
|
- "errors"
|
|
|
- "image"
|
|
|
- "image/draw"
|
|
|
- "image/jpeg"
|
|
|
- "image/png"
|
|
|
- "log"
|
|
|
- "path/filepath"
|
|
|
-
|
|
|
- "github.com/nfnt/resize"
|
|
|
- "imuslab.com/arozos/mod/filesystem"
|
|
|
-)
|
|
|
-
|
|
|
-/*
|
|
|
- Generate folder thumbnail from the containing files
|
|
|
- The preview is generated by overlapping 2 - 3 layers of images
|
|
|
-*/
|
|
|
-
|
|
|
-func generateThumbnailForFolder(fsh *filesystem.FileSystemHandler, cacheFolder string, file string, generateOnly bool) (string, error) {
|
|
|
- if fsh.RequireBuffer {
|
|
|
- return "", nil
|
|
|
- }
|
|
|
- fshAbs := fsh.FileSystemAbstraction
|
|
|
- //Check if this folder has cache image folder
|
|
|
- cacheFolderInsideThisFolder := filepath.Join(file, "/.metadata/.cache")
|
|
|
- if !fshAbs.FileExists(cacheFolderInsideThisFolder) {
|
|
|
- //This folder do not have a cache folder
|
|
|
- return "", errors.New("No previewable files")
|
|
|
- }
|
|
|
-
|
|
|
- //Load the base template
|
|
|
- if !fshAbs.FileExists("web/img/system/folder-preview.png") {
|
|
|
- //Missing system files. Skip rendering
|
|
|
- return "", errors.New("Missing system template image file")
|
|
|
- }
|
|
|
- image1, err := fshAbs.Open("web/img/system/folder-preview.png")
|
|
|
- if err != nil {
|
|
|
- return "", err
|
|
|
- }
|
|
|
-
|
|
|
- baseTemplate, err := png.Decode(image1)
|
|
|
- if err != nil {
|
|
|
- return "", err
|
|
|
- }
|
|
|
- image1.Close()
|
|
|
-
|
|
|
- //Generate the base image
|
|
|
- b := baseTemplate.Bounds()
|
|
|
- resultThumbnail := image.NewRGBA(b)
|
|
|
- draw.Draw(resultThumbnail, b, baseTemplate, image.ZP, draw.Over)
|
|
|
-
|
|
|
- //Get cached file inside this folder, only include jpg (non folder)
|
|
|
- contentCache, _ := fshAbs.Glob(filepath.Join(cacheFolderInsideThisFolder, "/*.jpg"))
|
|
|
-
|
|
|
- //Check if there are more than 1 file inside this folder that is cached
|
|
|
- if len(contentCache) > 1 {
|
|
|
- //More than 1 files. Render the image at the back
|
|
|
- image2, err := fshAbs.Open(contentCache[1])
|
|
|
- if err != nil {
|
|
|
- return "", err
|
|
|
- }
|
|
|
- backImage, err := jpeg.Decode(image2)
|
|
|
- if err != nil {
|
|
|
- return "", err
|
|
|
- }
|
|
|
- backImgOffset := image.Pt(155, 110)
|
|
|
- defer image2.Close()
|
|
|
- resizedBackImg := resize.Resize(250, 250, backImage, resize.Lanczos3)
|
|
|
- draw.Draw(resultThumbnail, resizedBackImg.Bounds().Add(backImgOffset), resizedBackImg, image.ZP, draw.Over)
|
|
|
- } else {
|
|
|
- //Nothing to preview inside this folder
|
|
|
- return "", errors.New("No previewable files")
|
|
|
- }
|
|
|
-
|
|
|
- //Render the top image
|
|
|
- image3, err := fshAbs.Open(contentCache[0])
|
|
|
- if err != nil {
|
|
|
- return "", errors.New("failed to open: " + err.Error())
|
|
|
- }
|
|
|
-
|
|
|
- topImage, err := jpeg.Decode(image3)
|
|
|
- if err != nil {
|
|
|
- //Fail to decode the image. Try to remove the damaged iamge file
|
|
|
- image3.Close()
|
|
|
- fshAbs.Remove(contentCache[0])
|
|
|
- log.Println("Failed to decode cahce image for: " + contentCache[0] + ". Removing thumbnail cache")
|
|
|
- return "", errors.New("failed to decode: " + err.Error())
|
|
|
- }
|
|
|
- defer image3.Close()
|
|
|
-
|
|
|
- topImageOffset := image.Pt(210, 210)
|
|
|
- resizedTopImage := resize.Resize(260, 260, topImage, resize.Lanczos3)
|
|
|
- draw.Draw(resultThumbnail, resizedTopImage.Bounds().Add(topImageOffset), resizedTopImage, image.ZP, draw.Over)
|
|
|
-
|
|
|
- outfile, err := fshAbs.Create(filepath.Join(cacheFolder, filepath.Base(file)+".png"))
|
|
|
- if err != nil {
|
|
|
- log.Fatalf("failed to create: %s", err)
|
|
|
- }
|
|
|
- png.Encode(outfile, resultThumbnail)
|
|
|
- outfile.Close()
|
|
|
-
|
|
|
- ctx, err := getImageAsBase64(fsh, cacheFolder+filepath.Base(file)+".png")
|
|
|
- return ctx, err
|
|
|
-}
|
|
|
+package metadata
|
|
|
+
|
|
|
+import (
|
|
|
+ "encoding/base64"
|
|
|
+ "errors"
|
|
|
+ "image"
|
|
|
+ "image/draw"
|
|
|
+ "image/jpeg"
|
|
|
+ "image/png"
|
|
|
+ "log"
|
|
|
+ "os"
|
|
|
+ "path/filepath"
|
|
|
+ "strings"
|
|
|
+
|
|
|
+ "github.com/nfnt/resize"
|
|
|
+ "imuslab.com/arozos/mod/filesystem"
|
|
|
+ "imuslab.com/arozos/mod/utils"
|
|
|
+)
|
|
|
+
|
|
|
+/*
|
|
|
+ Generate folder thumbnail from the containing files
|
|
|
+ The preview is generated by overlapping 2 - 3 layers of images
|
|
|
+*/
|
|
|
+
|
|
|
+func generateThumbnailForFolder(fsh *filesystem.FileSystemHandler, cacheFolder string, file string, generateOnly bool) (string, error) {
|
|
|
+ if fsh.RequireBuffer {
|
|
|
+ return "", nil
|
|
|
+ }
|
|
|
+ fshAbs := fsh.FileSystemAbstraction
|
|
|
+ //Check if this folder has cache image folder
|
|
|
+ cacheFolderInsideThisFolder := filepath.Join(file, "/.metadata/.cache")
|
|
|
+ if !fshAbs.FileExists(cacheFolderInsideThisFolder) {
|
|
|
+ //This folder do not have a cache folder
|
|
|
+ succ := generateLayeredThumbnailFolder(fsh, file)
|
|
|
+ if !succ {
|
|
|
+ return "", errors.New("failed to generate layered thumbnails inside folder")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //Load the base template
|
|
|
+ if !utils.FileExists("web/img/system/folder-preview.png") {
|
|
|
+ //Missing system files. Skip rendering
|
|
|
+ return "", errors.New("missing system template image file")
|
|
|
+ }
|
|
|
+ image1, err := os.Open("web/img/system/folder-preview.png")
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+
|
|
|
+ baseTemplate, err := png.Decode(image1)
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+ image1.Close()
|
|
|
+
|
|
|
+ //Generate the base image
|
|
|
+ b := baseTemplate.Bounds()
|
|
|
+ resultThumbnail := image.NewRGBA(b)
|
|
|
+ draw.Draw(resultThumbnail, b, baseTemplate, image.ZP, draw.Over)
|
|
|
+
|
|
|
+ //Get cached file inside this folder, only include jpg (non folder)
|
|
|
+ contentCache, _ := fshAbs.Glob(filepath.Join(cacheFolderInsideThisFolder, "/*.jpg"))
|
|
|
+
|
|
|
+ //Check if there are more than 1 file inside this folder that is cached
|
|
|
+ if len(contentCache) > 1 {
|
|
|
+ //More than 1 files. Render the image at the back
|
|
|
+ image2, err := fshAbs.Open(contentCache[1])
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+ backImage, err := jpeg.Decode(image2)
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+ backImgOffset := image.Pt(155, 110)
|
|
|
+ defer image2.Close()
|
|
|
+ resizedBackImg := resize.Resize(250, 250, backImage, resize.Lanczos3)
|
|
|
+ draw.Draw(resultThumbnail, resizedBackImg.Bounds().Add(backImgOffset), resizedBackImg, image.Point{}, draw.Over)
|
|
|
+ } else {
|
|
|
+ //Nothing to preview inside this folder, check if the folder is really empty
|
|
|
+ filesInside, _ := fshAbs.ReadDir(file)
|
|
|
+ if len(filesInside) > 1 {
|
|
|
+ //Not only the metadata folder is inside
|
|
|
+ templateFile := "web/img/system/folder-content.png"
|
|
|
+ if utils.FileExists(templateFile) {
|
|
|
+ content, err := os.ReadFile(templateFile)
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+ encoded := base64.StdEncoding.EncodeToString(content)
|
|
|
+ return string(encoded), nil
|
|
|
+ } else {
|
|
|
+ return "", errors.New("missing system template image file")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return "", errors.New("no previewable files")
|
|
|
+ }
|
|
|
+
|
|
|
+ //Render the top image
|
|
|
+ image3, err := fshAbs.Open(contentCache[0])
|
|
|
+ if err != nil {
|
|
|
+ return "", errors.New("failed to open: " + err.Error())
|
|
|
+ }
|
|
|
+
|
|
|
+ topImage, err := jpeg.Decode(image3)
|
|
|
+ if err != nil {
|
|
|
+ //Fail to decode the image. Try to remove the damaged iamge file
|
|
|
+ image3.Close()
|
|
|
+ fshAbs.Remove(contentCache[0])
|
|
|
+ log.Println("Failed to decode cahce image for: " + contentCache[0] + ". Removing thumbnail cache")
|
|
|
+ return "", errors.New("failed to decode: " + err.Error())
|
|
|
+ }
|
|
|
+ defer image3.Close()
|
|
|
+
|
|
|
+ topImageOffset := image.Pt(210, 210)
|
|
|
+ resizedTopImage := resize.Resize(260, 260, topImage, resize.Lanczos3)
|
|
|
+ draw.Draw(resultThumbnail, resizedTopImage.Bounds().Add(topImageOffset), resizedTopImage, image.Point{}, draw.Over)
|
|
|
+
|
|
|
+ outfile, err := fshAbs.Create(filepath.Join(cacheFolder, filepath.Base(file)+".png"))
|
|
|
+ if err != nil {
|
|
|
+ log.Fatalf("failed to create: %s", err)
|
|
|
+ }
|
|
|
+ png.Encode(outfile, resultThumbnail)
|
|
|
+ outfile.Close()
|
|
|
+
|
|
|
+ ctx, err := getImageAsBase64(fsh, cacheFolder+filepath.Base(file)+".png")
|
|
|
+ return ctx, err
|
|
|
+}
|
|
|
+
|
|
|
+// generateLayeredThumbnailFolder generate 2 thumbnails inside that folder
|
|
|
+// for folder thumbnail render
|
|
|
+func generateLayeredThumbnailFolder(fsh *filesystem.FileSystemHandler, file string) bool {
|
|
|
+ if fsh.RequireBuffer {
|
|
|
+ //Too much work for buffer mode, skip generating
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ fshAbs := fsh.FileSystemAbstraction
|
|
|
+ //Check if this folder has cache image folder
|
|
|
+ cacheFolderInsideThisFolder := filepath.Join(file, "/.metadata/.cache")
|
|
|
+ if !fshAbs.FileExists(cacheFolderInsideThisFolder) {
|
|
|
+ //Create the cache folder
|
|
|
+ err := fshAbs.MkdirAll(cacheFolderInsideThisFolder, 0755)
|
|
|
+ if err != nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //List all files in the folder
|
|
|
+ files, err := fshAbs.ReadDir(file)
|
|
|
+ if err != nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ //Generate thumbnails for the first 2 image files
|
|
|
+ imageExtensions := []string{".jpg", ".jpeg", ".png"}
|
|
|
+ generatedCount := 0
|
|
|
+ for _, fi := range files {
|
|
|
+ if fi.IsDir() || fi.Name()[0] == '.' {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ ext := strings.ToLower(filepath.Ext(fi.Name()))
|
|
|
+ isImage := false
|
|
|
+ for _, imgExt := range imageExtensions {
|
|
|
+ if ext == imgExt {
|
|
|
+ isImage = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if isImage {
|
|
|
+ filePath := filepath.Join(file, fi.Name())
|
|
|
+ // Generate thumbnail for this image into the cache folder
|
|
|
+ _, err := generateThumbnailForImage(fsh, cacheFolderInsideThisFolder+"/", filePath, false)
|
|
|
+ if err == nil {
|
|
|
+ generatedCount++
|
|
|
+ if generatedCount >= 2 {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if generatedCount == 0 {
|
|
|
+ // -1 because there always will be at least 1 folder (the metadata folder)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ return true
|
|
|
+}
|