comparison sat/plugins/plugin_misc_attach.py @ 3223:163014f09bf4

plugin attach: handle large images resizing: if `C.MESS_KEY_ATTACHMENTS_RESIZE` flag is set for an attachment, it will be checked, and if it is a large image, a resized copy will be uploaded.
author Goffi <goffi@goffi.org>
date Sun, 22 Mar 2020 14:04:16 +0100
parents 2ba602aef90e
children be6d91572633
comparison
equal deleted inserted replaced
3222:653fa213d2f8 3223:163014f09bf4
14 # GNU Affero General Public License for more details. 14 # GNU Affero General Public License for more details.
15 15
16 # You should have received a copy of the GNU Affero General Public License 16 # You should have received a copy of the GNU Affero General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. 17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 18
19 from functools import partial
20 from pathlib import Path 19 from pathlib import Path
21 from collections import namedtuple 20 from collections import namedtuple
22 from twisted.internet import defer 21 from twisted.internet import defer
22 import mimetypes
23 import tempfile
24 import shutil
23 from sat.core.i18n import _ 25 from sat.core.i18n import _
24 from sat.core import exceptions 26 from sat.core import exceptions
25 from sat.core.constants import Const as C 27 from sat.core.constants import Const as C
26 from sat.core.log import getLogger 28 from sat.core.log import getLogger
27 from sat.tools import utils 29 from sat.tools import utils
30 from sat.tools import image
28 31
29 32
30 log = getLogger(__name__) 33 log = getLogger(__name__)
31 34
32 35
84 handlers.append(handler) 87 handlers.append(handler)
85 handlers.sort(key=lambda h: h.priority, reverse=True) 88 handlers.sort(key=lambda h: h.priority, reverse=True)
86 log.debug(f"new attachments handler: {handler}") 89 log.debug(f"new attachments handler: {handler}")
87 90
88 async def attachFiles(self, client, data): 91 async def attachFiles(self, client, data):
92 """Main method to attach file
93
94 It will do generic pre-treatment, and call the suitable attachments handler
95 """
96 # we check attachment for pre-treatment like large image resizing
97 # media_type will be added if missing (and if it can be guessed from path)
98 attachments = data["extra"][C.MESS_KEY_ATTACHMENTS]
99 tmp_dirs_to_clean = []
100 for attachment in attachments:
101 if attachment.get(C.MESS_KEY_ATTACHMENTS_RESIZE, False):
102 path = Path(attachment["path"])
103 try:
104 media_type = attachment[C.MESS_KEY_ATTACHMENTS_MEDIA_TYPE]
105 except KeyError:
106 media_type = mimetypes.guess_type(path, strict=False)[0]
107 if media_type is None:
108 log.warning(
109 _("Can't resize attachment of unknown type: {attachment}")
110 .format(attachment=attachment))
111 continue
112 attachment[C.MESS_KEY_ATTACHMENTS_MEDIA_TYPE] = media_type
113
114 main_type = media_type.split('/')[0]
115 if main_type == "image":
116 report = image.check(self.host, path)
117 if report['too_large']:
118 tmp_dir = Path(tempfile.mkdtemp())
119 tmp_dirs_to_clean.append(tmp_dir)
120 new_path = tmp_dir / path.name
121 await image.resize(
122 path, report["recommended_size"], dest=new_path)
123 attachment["path"] = new_path
124 log.info(
125 _("Attachment {path!r} has been resized at {new_path!r}")
126 .format(path=str(path), new_path=str(new_path)))
127 else:
128 log.warning(
129 _("Can't resize attachment of type {main_type!r}: {attachment}")
130 .format(main_type=main_type, attachment=attachment))
131
89 if client.encryption.isEncryptionRequested(data): 132 if client.encryption.isEncryptionRequested(data):
90 handlers = self._attachments_handlers['encrypted'] 133 handlers = self._attachments_handlers['encrypted']
91 else: 134 else:
92 handlers = self._attachments_handlers['clear'] 135 handlers = self._attachments_handlers['clear']
93 136
100 _("No plugin can handle attachment with {destinee}").format( 143 _("No plugin can handle attachment with {destinee}").format(
101 destinee = data['to'] 144 destinee = data['to']
102 )) 145 ))
103 146
104 await utils.asDeferred(handler.attach, client, data) 147 await utils.asDeferred(handler.attach, client, data)
148
149 for dir_path in tmp_dirs_to_clean:
150 log.debug(f"Cleaning temporary directory at {dir_path}")
151 shutil.rmtree(dir_path)
105 152
106 return data 153 return data
107 154
108 async def uploadFiles(self, client, data, upload_cb=None): 155 async def uploadFiles(self, client, data, upload_cb=None):
109 """Upload file, and update attachments 156 """Upload file, and update attachments
184 231
185 attachment["url"] = ret 232 attachment["url"] = ret
186 233
187 return data 234 return data
188 235
189 def _attachFiles(self, client, data): 236 def _attachFiles(self, data, client):
190 return defer.ensureDeferred(self.attachFiles(client, data)) 237 return defer.ensureDeferred(self.attachFiles(client, data))
191 238
192 def _sendMessageTrigger( 239 def _sendMessageTrigger(
193 self, client, mess_data, pre_xml_treatments, post_xml_treatments): 240 self, client, mess_data, pre_xml_treatments, post_xml_treatments):
194 if mess_data['extra'].get(C.MESS_KEY_ATTACHMENTS): 241 if mess_data['extra'].get(C.MESS_KEY_ATTACHMENTS):
195 post_xml_treatments.addCallback(partial(self._attachFiles, client)) 242 post_xml_treatments.addCallback(self._attachFiles, client=client)
196 return True 243 return True
197 244
198 async def defaultCanHandle(self, client, data): 245 async def defaultCanHandle(self, client, data):
199 return True 246 return True
200 247