瀏覽代碼

Added experimental folder preview thumbnail generator

tobychui 4 年之前
父節點
當前提交
dc63c898ad

+ 8 - 1
file_system.go

@@ -2419,8 +2419,15 @@ func system_fs_handleCacheRender(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	//Get folder sort mode
+	sortMode := "default"
+	folder := filepath.ToSlash(filepath.Clean(vpath))
+	if sysdb.KeyExists("fs-sortpref", userinfo.Username+"/"+folder) {
+		sysdb.Read("fs-sortpref", userinfo.Username+"/"+folder, &sortMode)
+	}
+
 	//Perform cache rendering
-	thumbRenderHandler.HandleLoadCache(w, r, rpath)
+	thumbRenderHandler.HandleLoadCache(w, r, rpath, sortMode)
 }
 
 //Handle loading of one thumbnail

+ 12 - 0
mod/filesystem/metadata/common.go

@@ -4,6 +4,7 @@ import (
 	"bufio"
 	"encoding/base64"
 	"errors"
+	"fmt"
 	"io/ioutil"
 	"log"
 	"net/http"
@@ -133,3 +134,14 @@ func removeFromSliceIfExists(slice []string, target string) []string {
 
 	return newSlice
 }
+
+func mtime(filename string) int64 {
+	file, err := os.Stat(filename)
+
+	if err != nil {
+		fmt.Println(err)
+	}
+
+	modifiedtime := file.ModTime()
+	return modifiedtime.Unix()
+}

+ 94 - 0
mod/filesystem/metadata/folder.go

@@ -0,0 +1,94 @@
+package metadata
+
+import (
+	"errors"
+	"image"
+	"image/draw"
+	"image/jpeg"
+	"image/png"
+	"log"
+	"os"
+	"path/filepath"
+
+	"github.com/nfnt/resize"
+)
+
+/*
+	Generate folder thumbnail from the containing files
+	The preview is generated by overlapping 2 - 3 layers of images
+*/
+
+func generateThumbnailForFolder(cacheFolder string, file string, generateOnly bool) (string, error) {
+	//Check if this folder has cache image folder
+	cacheFolderInsideThisFolder := filepath.Join(file, "/.cache")
+	if !fileExists(cacheFolderInsideThisFolder) {
+		//This folder do not have a cache folder
+		return "", errors.New("No previewable files")
+	}
+
+	//Load the base template
+	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
+	contentCache, _ := filepath.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 := os.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 if len(contentCache) == 0 {
+		//Nothing to preview inside this folder
+		return "", errors.New("No previewable files")
+	}
+
+	//Render the top image
+	image3, err := os.Open(contentCache[0])
+	if err != nil {
+		log.Fatalf("failed to open: %s", err)
+	}
+
+	topImage, err := jpeg.Decode(image3)
+	if err != nil {
+		log.Fatalf("failed to decode: %s", err)
+	}
+	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 := os.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(cacheFolder + filepath.Base(file) + ".png")
+	return ctx, err
+}

+ 55 - 16
mod/filesystem/metadata/metadata.go

@@ -16,6 +16,7 @@ import (
 
 	"github.com/gorilla/websocket"
 
+	"imuslab.com/arozos/mod/filesystem/fssort"
 	hidden "imuslab.com/arozos/mod/filesystem/hidden"
 )
 
@@ -60,7 +61,7 @@ func (rh *RenderHandler) BuildCacheForFolder(path string) error {
 
 func (rh *RenderHandler) CacheExists(file string) bool {
 	cacheFolder := filepath.ToSlash(filepath.Clean(filepath.Dir(file))) + "/.cache/"
-	return fileExists(cacheFolder + filepath.Base(file) + ".jpg")
+	return fileExists(cacheFolder+filepath.Base(file)+".jpg") || fileExists(cacheFolder+filepath.Base(file)+".png")
 }
 
 //Try to load a cache from file. If not exists, generate it now
@@ -72,28 +73,41 @@ func (rh *RenderHandler) LoadCache(file string, generateOnly bool) (string, erro
 	hidden.HideFile(cacheFolder)
 
 	//Check if cache already exists. If yes, return the image from the cache folder
-	if fileExists(cacheFolder + filepath.Base(file) + ".jpg") {
+	if fileExists(cacheFolder+filepath.Base(file)+".jpg") || fileExists(cacheFolder+filepath.Base(file)+".png") {
 		if generateOnly {
 			//Only generate, do not return image
 			return "", nil
 		}
 
-		//Check if the file is being writting by another process. If yes, wait for it
-		counter := 0
-		for rh.fileIsBusy(file) && counter < 15 {
-			counter += 1
-			time.Sleep(1 * time.Second)
+		//Allow thumbnail to be either jpg or png file
+		ext := ".jpg"
+		if !fileExists(cacheFolder + filepath.Base(file) + ".jpg") {
+			ext = ".png"
 		}
 
-		//Time out and the file is still busy
-		if rh.fileIsBusy(file) {
-			log.Println("Process racing for cache file. Skipping", file)
-			return "", errors.New("Process racing for cache file. Skipping")
+		//Updates 02/10/2021: Check if the source file is newer than the cache. Update the cache if true
+		if mtime(file) > mtime(cacheFolder+filepath.Base(file)+ext) {
+			//File is newer than cache. Delete the cache
+			os.Remove(cacheFolder + filepath.Base(file) + ext)
+		} else {
+			//Check if the file is being writting by another process. If yes, wait for it
+			counter := 0
+			for rh.fileIsBusy(file) && counter < 15 {
+				counter += 1
+				time.Sleep(1 * time.Second)
+			}
+
+			//Time out and the file is still busy
+			if rh.fileIsBusy(file) {
+				log.Println("Process racing for cache file. Skipping", file)
+				return "", errors.New("Process racing for cache file. Skipping")
+			}
+
+			//Read and return the image
+			ctx, err := getImageAsBase64(cacheFolder + filepath.Base(file) + ext)
+			return ctx, err
 		}
 
-		//Read and return the image
-		ctx, err := getImageAsBase64(cacheFolder + filepath.Base(file) + ".jpg")
-		return ctx, err
 	} else {
 		//This file not exists yet. Check if it is being hold by another process already
 		if rh.fileIsBusy(file) {
@@ -135,6 +149,13 @@ func (rh *RenderHandler) LoadCache(file string, generateOnly bool) (string, erro
 		return img, err
 	}
 
+	//Folder preview renderer
+	if isDir(file) && len(filepath.Base(file)) > 0 && filepath.Base(file)[:1] != "." {
+		img, err := generateThumbnailForFolder(cacheFolder, file, generateOnly)
+		rh.renderingFiles.Delete(file)
+		return img, err
+	}
+
 	//Other filters
 	rh.renderingFiles.Delete(file)
 	return "", errors.New("No supported format")
@@ -169,8 +190,8 @@ func getImageAsBase64(path string) (string, error) {
 	return string(encoded), nil
 }
 
-//Load a list of folder cache from websocket
-func (rh *RenderHandler) HandleLoadCache(w http.ResponseWriter, r *http.Request, rpath string) {
+//Load a list of folder cache from websocket, pass in "" (empty string) for default sorting method
+func (rh *RenderHandler) HandleLoadCache(w http.ResponseWriter, r *http.Request, rpath string, sortmode string) {
 	//Get a list of files pending to be cached and sent
 	targetPath := filepath.ToSlash(filepath.Clean(rpath))
 
@@ -206,6 +227,24 @@ func (rh *RenderHandler) HandleLoadCache(w http.ResponseWriter, r *http.Request,
 	errorExists := false
 	filesWithoutCache := []string{}
 
+	//Updates implementation 02/10/2021: Load thumbnail of files first before folder and apply user preference sort mode
+	if sortmode == "" {
+		sortmode = "default"
+	}
+
+	pendingFiles := []string{}
+	pendingFolders := []string{}
+	for _, file := range files {
+		if isDir(file) {
+			pendingFiles = append(pendingFiles, file)
+		} else {
+			pendingFolders = append(pendingFolders, file)
+		}
+	}
+	pendingFiles = append(pendingFiles, pendingFolders...)
+
+	files = fssort.SortFileList(pendingFiles, sortmode)
+
 	//Updated implementation 24/12/2020: Load image with cache first before rendering those without
 
 	for _, file := range files {

File diff suppressed because it is too large
+ 27 - 0
web/img/system/folder-preview.ai


二進制
web/img/system/folder-preview.png


File diff suppressed because it is too large
+ 3 - 3
web/img/system/nas.ai


+ 2 - 2
web/img/system/nas.svg

@@ -3,10 +3,10 @@
 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
 <svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 	 width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
-<rect x="15.49" y="12.625" fill="#727171" width="79.344" height="102.833"/>
+<rect x="15.49" y="12.625" fill="#3E3A39" width="79.344" height="102.833"/>
 <path fill="#FFFFFF" d="M41.144,105.691c0,3.358,2.174,6.082,4.856,6.082h40.651c2.68,0,4.854-2.724,4.854-6.082V33.053
 	c0-3.359-2.175-6.083-4.854-6.083H46c-2.682,0-4.856,2.724-4.856,6.083V105.691z"/>
-<polygon fill="#DCDDDD" points="112.51,96.458 112.51,30.292 94.834,12.626 94.834,115.458 "/>
+<polygon fill="#C9CACA" points="112.51,96.458 112.51,30.292 94.834,12.626 94.834,115.458 "/>
 <path fill="#3E3A39" d="M70.303,109.527c-1.399,0-2.532-1.1-2.532-2.457V32.105c0-1.357,1.133-2.458,2.532-2.458h16.044
 	c1.4,0,2.533,1.101,2.533,2.458v74.965c0,1.359-1.133,2.459-2.533,2.459L70.303,109.527z"/>
 <circle fill="#00A0E9" cx="78.326" cy="39.904" r="3.708"/>

Some files were not shown because too many files changed in this diff