Преглед на файлове

Optimized image thumbnail generation RAM utilization

Toby Chui преди 2 години
родител
ревизия
f614957097

+ 82 - 65
mod/filesystem/metadata/image.go

@@ -1,65 +1,82 @@
-package metadata
-
-import (
-	"bytes"
-	"image"
-	"image/jpeg"
-	"path/filepath"
-
-	"github.com/nfnt/resize"
-	"github.com/oliamb/cutter"
-	"imuslab.com/arozos/mod/filesystem"
-)
-
-func generateThumbnailForImage(fsh *filesystem.FileSystemHandler, cacheFolder string, file string, generateOnly bool) (string, error) {
-	if fsh.RequireBuffer {
-		return "", nil
-	}
-	fshAbs := fsh.FileSystemAbstraction
-	imageBytes, err := fshAbs.ReadFile(file)
-	if err != nil {
-		return "", err
-	}
-	//Resize to desiered width
-	img, _, err := image.Decode(bytes.NewReader(imageBytes))
-	if err != nil {
-		return "", err
-	}
-
-	//Check boundary to decide resize mode
-	b := img.Bounds()
-	imgWidth := b.Max.X
-	imgHeight := b.Max.Y
-
-	var m image.Image
-	if imgWidth > imgHeight {
-		m = resize.Resize(0, 480, img, resize.Lanczos3)
-	} else {
-		m = resize.Resize(480, 0, img, resize.Lanczos3)
-	}
-
-	//Crop out the center
-	croppedImg, err := cutter.Crop(m, cutter.Config{
-		Width:  480,
-		Height: 480,
-		Mode:   cutter.Centered,
-	})
-
-	//Create the thumbnail
-	out, err := fshAbs.Create(cacheFolder + filepath.Base(file) + ".jpg")
-	if err != nil {
-		return "", err
-	}
-
-	// write new image to file
-	jpeg.Encode(out, croppedImg, nil)
-	out.Close()
-
-	if !generateOnly {
-		//return the image as well
-		ctx, err := getImageAsBase64(fsh, cacheFolder+filepath.Base(file)+".jpg")
-		return ctx, err
-	} else {
-		return "", nil
-	}
-}
+package metadata
+
+import (
+	"bytes"
+	"image"
+	"image/jpeg"
+	"os"
+	"path/filepath"
+
+	"github.com/nfnt/resize"
+	"github.com/oliamb/cutter"
+	"imuslab.com/arozos/mod/filesystem"
+)
+
+//Generate thumbnail for image. Require real filepath
+func generateThumbnailForImage(fsh *filesystem.FileSystemHandler, cacheFolder string, file string, generateOnly bool) (string, error) {
+	if fsh.RequireBuffer {
+		return "", nil
+	}
+	fshAbs := fsh.FileSystemAbstraction
+	var img image.Image
+	var err error
+	if fsh.RequireBuffer {
+		//This fsh is remote. Buffer to RAM
+		imageBytes, err := fshAbs.ReadFile(file)
+		if err != nil {
+			return "", err
+		}
+		img, _, err = image.Decode(bytes.NewReader(imageBytes))
+		if err != nil {
+			return "", err
+		}
+	} else {
+		srcImage, err := fshAbs.OpenFile(file, os.O_RDONLY, 0775)
+		if err != nil {
+			return "", err
+		}
+		defer srcImage.Close()
+		img, _, err = image.Decode(srcImage)
+		if err != nil {
+			return "", err
+		}
+	}
+
+	//Resize to desiered width
+	//Check boundary to decide resize mode
+	b := img.Bounds()
+	imgWidth := b.Max.X
+	imgHeight := b.Max.Y
+
+	var m image.Image
+	if imgWidth > imgHeight {
+		m = resize.Resize(0, 480, img, resize.Lanczos3)
+	} else {
+		m = resize.Resize(480, 0, img, resize.Lanczos3)
+	}
+
+	//Crop out the center
+	croppedImg, err := cutter.Crop(m, cutter.Config{
+		Width:  480,
+		Height: 480,
+		Mode:   cutter.Centered,
+	})
+
+	//Create the thumbnail
+	out, err := fshAbs.Create(cacheFolder + filepath.Base(file) + ".jpg")
+	if err != nil {
+		return "", err
+	}
+
+	// write new image to file
+	jpeg.Encode(out, croppedImg, nil)
+	out.Close()
+
+	if !generateOnly {
+		//return the image as well
+		ctx, err := getImageAsBase64(fsh, cacheFolder+filepath.Base(file)+".jpg")
+		return ctx, err
+	} else {
+		return "", nil
+	}
+}

+ 62 - 0
web/Message/backend/rpc.js

@@ -0,0 +1,62 @@
+/*
+    rpc.js
+
+    This script do the following stuffs
+    - set a user online status 
+    - get the latest message id (if channel id is given)
+    - set a user state (if channel id is given)
+
+    Required paramters:
+    channel (optional): Channel ID
+    userstate (optional): {typing / standby / uploading}
+*/
+
+var username = USERNAME;
+newDBTableIfNotExists("message")
+
+//Set this user last online time
+writeDBItem("message", "online_status/" + username, Math.floor(Date.now() / 1000));
+
+//Check if channel id is defined
+if (typeof(channel) != "undefined" && channel != ""){
+    //In channel. Get the ID out of this user pair
+    var channelID = channel + "_" + username;
+    if (username < channel){
+        channelID = username + "_" + channel
+    }
+
+    //Get opposite online time if exists
+    var oppositeOnlineTime = readDBItem("message", "online_status/" + channel)
+    if (oppositeOnlineTime != ""){
+        oppositeOnlineTime = parseInt(oppositeOnlineTime);
+    }else{
+        oppositeOnlineTime = -1;
+    }
+
+    //Prepare the data structure to be returned
+    var resultingStruct = {
+        latestMessageId: "",
+        oppositeLastOnlineTime: oppositeOnlineTime,
+        oppositeStatus: ""
+    };
+
+    
+    //Check the latest message id
+    var latestMessage = readDBItem("message", "latest_id/" + channelID);
+    var message = [];
+    if (latestMessage == ""){
+        //No message 
+
+    }else{
+        resultingStruct.latestMessageId = latestMessage;
+    }
+
+    sendJSONResp(resultingStruct);
+
+}else if (typeof(group) != "undefined" && group != ""){
+    //Group function, to be developed
+
+}else{
+    //Homepage. Show all chat updates
+
+}

+ 65 - 5
web/Message/chat.html

@@ -9,6 +9,7 @@
 		<script src="../script/jquery.min.js"></script>
 		<script src="../script/ao_module.js"></script>
 		<script src="../script/semantic/semantic.min.js"></script>
+        <script src="message.js"></script>
 		<link rel="icon" type="image/png" href="img/module_icon.png">
 		<link rel="manifest" crossorigin="use-credentials" href="manifest.json">
 		<title>Message</title>
@@ -63,12 +64,23 @@
             .chatbubble .timestampWrapper{
                 height: 1em;
             }
-            
+            .sendbar{
+                position: fixed;
+                bottom: 0;
+                left: 0;
+                width: 100%;
+                padding: 0.4em;
+            }
 		</style>
 	</head>
 	<body>
-        <br>
+        <div class="ui segment" stlye="padding-top: 1em;">
+            <div style="width: 100%; height: 0.3em;"></div>
+            <img id="usericon" class="ui avatar mini image" style="margin-top: -0.7em; " src="img/noicon.png">
+            <div style="display: inline-block; font-size: 1.5rem; margin-left: 0.2em;"><i id="onlineStatus"></i><span id="username">Username</span></div>
+        </div>
 		<div id="chatroom" class="ui text container">
+            <!-- 
 			<div class="self chatdialog">
                 <div class="ui segment chatbubble">
                     <p>Hello World! How are you today?</p>
@@ -85,12 +97,60 @@
                     </div>
                 </div>
             </div>
-            
+            -->
 		</div>
-		<div class="ui divider"></div>
+        <div class="sendbar">
+            <div class="ui fluid action input">
+                <input id="msgInput" type="text" placeholder="Aa">
+                <button class="ui icon button" style="background-color: #608cfc; color: white;"><i class="chevron right icon"></i></button>
+            </div>
+        </div>
+		
 		<br><br>
 		<script>
-			
+            var username = "";
+			if (window.location.hash.length > 1){
+                username = window.location.hash.substr(1);
+                $("#username").text(username);
+
+                //Get the user icon
+                $.get("../system/users/list", function(data){
+					console.log(data);
+					data.forEach(function(user){
+						let thisUsername = user[0];
+						let usericon = user[2];
+                        if (thisUsername == username){
+                            $("#usericon").attr('src', usericon);
+                        }
+                    });
+                });
+
+                //Update the channel id, see message.js
+                channelID = username;
+                
+            }
+
+
+            function updateUserOnlineStatus(lastOnlineTimestamp){
+                var currentTime = Math.floor(Date.now() / 1000);
+                if (currentTime - lastOnlineTimestamp > 300){
+                    //Offline
+                    $("#onlineStatus").attr("class", "grey small circle icon");
+                }else{
+                    //Online
+                    $("#onlineStatus").attr("class", "green small circle icon");
+                }
+            }
+
+            //RPC callback events
+            rpcCallback = function(data){
+                console.log(data);
+                updateUserOnlineStatus(data.oppositeLastOnlineTime);
+            }
+
+            //Manually start the first request on page load
+            updateStatus();
+            
 		</script>
 	</body>
 </html>

+ 3 - 2
web/Message/index.html

@@ -74,8 +74,9 @@
 						if (usericon == ""){
 							usericon = "img/noicon.png";
 						}
+						let chatroomID = username;
 						$("#chatHistoryList").append(`<div class="ui message chatroom">
-						<div class="ui feed">
+						<a class="ui feed" href="chat.html#${chatroomID}">
 							<div class="event">
 								<div class="label" style="padding-top: 0.3em;">
 									<img src="${usericon}">
@@ -92,7 +93,7 @@
 								</div>
 							</div>
 							</div>
-						</div>
+						</a>
 					</div>`);
 					})
 				});

+ 34 - 0
web/Message/message.js

@@ -0,0 +1,34 @@
+/*
+    Common Message app Tools
+*/
+
+//Current watching channel, leave empty for all
+var channelID = "";
+var userstate = "standby"; //standby / typing / uploading
+//Current status return data from last rpc
+var statusData = {};
+var rpcCallback = undefined;
+
+//Setup timer to update the above two paramters
+setInterval(function(){
+    updateStatus();
+}, 5000);
+
+//Call this function on start
+function updateStatus(){
+    var messageInputValue = $("#msgInput").val();
+    if (messageInputValue.trim() != ""){
+        userstate = "typing";
+    }else{
+        userstate = "standby";
+    }
+    ao_module_agirun("Message/backend/rpc.js", {
+        channel: channelID,
+        userstate: userstate
+    }, function(data){
+        statusData = data;
+        if (rpcCallback != undefined){
+            rpcCallback(data);
+        }
+    });
+}

BIN
web/img/desktop/bg/nature/0.jpg


BIN
web/img/desktop/bg/snow/0.jpg


BIN
web/img/desktop/bg/snow/1.jpg


BIN
web/img/desktop/bg/snow/2.jpg


BIN
web/img/desktop/bg/snow/3.jpg


BIN
web/img/desktop/bg/valley/0.jpg


BIN
web/img/desktop/bg/valley/1.jpg


BIN
web/img/desktop/bg/valley/2.jpg


BIN
web/img/desktop/bg/valley/3.jpg