changeset 3201:439e2f88c3a9

core, bridge: new `imageGeneratePreview` helped method to generate a thumbnail
author Goffi <goffi@goffi.org>
date Sun, 01 Mar 2020 18:48:06 +0100
parents 5c3bf37f2202
children 2e892f9f54f6
files sat/bridge/bridge_constructor/bridge_template.ini sat/bridge/dbus_bridge.py sat/core/constants.py sat/core/sat_main.py sat_frontends/bridge/dbus_bridge.py sat_frontends/bridge/pb.py
diffstat 6 files changed, 90 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/sat/bridge/bridge_constructor/bridge_template.ini	Sun Mar 01 18:47:05 2020 +0100
+++ b/sat/bridge/bridge_constructor/bridge_template.ini	Sun Mar 01 18:48:06 2020 +0100
@@ -972,3 +972,14 @@
 doc_param_2=height: height of the new image
 doc_return=path of the new image with desired size
     the image must be deleted once not needed anymore
+
+[imageGeneratePreview]
+async=
+type=method
+category=core
+sig_in=ss
+sig_out=s
+doc=Generate a preview of an image in cache
+doc_param_0=image_path: path of the original image
+doc_param_1=%(doc_profile_key)s
+doc_return=path to the preview in cache
--- a/sat/bridge/dbus_bridge.py	Sun Mar 01 18:47:05 2020 +0100
+++ b/sat/bridge/dbus_bridge.py	Sun Mar 01 18:48:06 2020 +0100
@@ -397,6 +397,12 @@
         return self._callback("imageCheck", str(arg_0))
 
     @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
+                         in_signature='ss', out_signature='s',
+                         async_callbacks=('callback', 'errback'))
+    def imageGeneratePreview(self, image_path, profile_key, callback=None, errback=None):
+        return self._callback("imageGeneratePreview", str(image_path), str(profile_key), callback=callback, errback=errback)
+
+    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
                          in_signature='sii', out_signature='s',
                          async_callbacks=('callback', 'errback'))
     def imageResize(self, image_path, width, height, callback=None, errback=None):
--- a/sat/core/constants.py	Sun Mar 01 18:47:05 2020 +0100
+++ b/sat/core/constants.py	Sun Mar 01 18:48:06 2020 +0100
@@ -135,6 +135,7 @@
 
     MESS_KEY_ATTACHMENTS = "attachments"
     MESS_KEY_ATTACHMENTS_MEDIA_TYPE = "media_type"
+    MESS_KEY_ATTACHMENTS_PREVIEW = "preview"
 
     # File encryption algorithms
     ENC_AES_GCM = "AES-GCM"
--- a/sat/core/sat_main.py	Sun Mar 01 18:47:05 2020 +0100
+++ b/sat/core/sat_main.py	Sun Mar 01 18:48:06 2020 +0100
@@ -20,6 +20,8 @@
 import sys
 import os.path
 import uuid
+import hashlib
+from pathlib import Path
 import sat
 from sat.core.i18n import _, D_, languageSwitch
 from sat.core import patches
@@ -173,6 +175,7 @@
         self.bridge.register_method("namespacesGet", self.getNamespaces)
         self.bridge.register_method("imageCheck", self._imageCheck)
         self.bridge.register_method("imageResize", self._imageResize)
+        self.bridge.register_method("imageGeneratePreview", self._imageGeneratePreview)
 
         self.memory.initialized.addCallback(lambda __: defer.ensureDeferred(self._postMemoryInit()))
 
@@ -684,6 +687,45 @@
         d.addCallback(lambda new_image_path: str(new_image_path))
         return d
 
+    def _imageGeneratePreview(self, path, profile_key):
+        client = self.getClient(profile_key)
+        d = defer.ensureDeferred(self.imageGeneratePreview(client, Path(path)))
+        d.addCallback(lambda preview_path: str(preview_path))
+        return d
+
+    async def imageGeneratePreview(self, client, path):
+        """Helper method to generate in cache a preview of an image
+
+        @param path(Path): path to the image
+        @return (Path): path to the generated preview
+        """
+        report = images.checkImage(self, path, max_size=(300, 300))
+
+        if not report['too_large']:
+            # in the unlikely case that image is already smaller than a preview
+            preview_path = path
+        else:
+            # we use hash as id, to re-use potentially existing preview
+            path_hash = hashlib.sha256(str(path).encode()).hexdigest()
+            uid = f"{path.stem}_{path_hash}_preview"
+            filename = f"{uid}{path.suffix.lower()}"
+            metadata = client.cache.getMetadata(uid=uid)
+            if metadata is not None:
+                preview_path = metadata['path']
+            else:
+                with client.cache.cacheData(
+                    source='HOST_PREVIEW',
+                    uid=uid,
+                    filename=filename) as cache_f:
+
+                    preview_path = await images.resizeImage(
+                        path,
+                        new_size=report['recommended_size'],
+                        dest=cache_f
+                    )
+
+        return preview_path
+
     # local dirs
 
     def getLocalPath(self, client, dir_name, *extra_path, **kwargs):
--- a/sat_frontends/bridge/dbus_bridge.py	Sun Mar 01 18:47:05 2020 +0100
+++ b/sat_frontends/bridge/dbus_bridge.py	Sun Mar 01 18:48:06 2020 +0100
@@ -489,6 +489,15 @@
             kwargs['error_handler'] = error_handler
         return str(self.db_core_iface.imageCheck(arg_0, **kwargs))
 
+    def imageGeneratePreview(self, image_path, profile_key, callback=None, errback=None):
+        if callback is None:
+            error_handler = None
+        else:
+            if errback is None:
+                errback = log.error
+            error_handler = lambda err:errback(dbus_to_bridge_exception(err))
+        return str(self.db_core_iface.imageGeneratePreview(image_path, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler))
+
     def imageResize(self, image_path, width, height, callback=None, errback=None):
         if callback is None:
             error_handler = None
@@ -1149,6 +1158,14 @@
         self.db_core_iface.imageCheck(arg_0, timeout=const_TIMEOUT, reply_handler=reply_handler, error_handler=error_handler)
         return fut
 
+    def imageGeneratePreview(self, image_path, profile_key):
+        loop = asyncio.get_running_loop()
+        fut = loop.create_future()
+        reply_handler = lambda ret=None: loop.call_soon_threadsafe(fut.set_result, ret)
+        error_handler = lambda err: loop.call_soon_threadsafe(fut.set_exception, dbus_to_bridge_exception(err))
+        self.db_core_iface.imageGeneratePreview(image_path, profile_key, timeout=const_TIMEOUT, reply_handler=reply_handler, error_handler=error_handler)
+        return fut
+
     def imageResize(self, image_path, width, height):
         loop = asyncio.get_running_loop()
         fut = loop.create_future()
--- a/sat_frontends/bridge/pb.py	Sun Mar 01 18:47:05 2020 +0100
+++ b/sat_frontends/bridge/pb.py	Sun Mar 01 18:48:06 2020 +0100
@@ -383,6 +383,14 @@
             errback = self._generic_errback
         d.addErrback(errback)
 
+    def imageGeneratePreview(self, image_path, profile_key, callback=None, errback=None):
+        d = self.root.callRemote("imageGeneratePreview", image_path, profile_key)
+        if callback is not None:
+            d.addCallback(callback)
+        if errback is None:
+            errback = self._generic_errback
+        d.addErrback(errback)
+
     def imageResize(self, image_path, width, height, callback=None, errback=None):
         d = self.root.callRemote("imageResize", image_path, width, height)
         if callback is not None:
@@ -818,6 +826,11 @@
         d.addErrback(self._errback)
         return d.asFuture(asyncio.get_event_loop())
 
+    def imageGeneratePreview(self, image_path, profile_key):
+        d = self.root.callRemote("imageGeneratePreview", image_path, profile_key)
+        d.addErrback(self._errback)
+        return d.asFuture(asyncio.get_event_loop())
+
     def imageResize(self, image_path, width, height):
         d = self.root.callRemote("imageResize", image_path, width, height)
         d.addErrback(self._errback)