summary refs log tree commit diff
path: root/synapse/media
diff options
context:
space:
mode:
Diffstat (limited to 'synapse/media')
-rw-r--r--synapse/media/v0/content_repository.py2
-rw-r--r--synapse/media/v1/__init__.py45
-rw-r--r--synapse/media/v1/base_resource.py15
-rw-r--r--synapse/media/v1/download_resource.py14
-rw-r--r--synapse/media/v1/filepath.py2
-rw-r--r--synapse/media/v1/media_repository.py2
-rw-r--r--synapse/media/v1/thumbnail_resource.py27
-rw-r--r--synapse/media/v1/thumbnailer.py10
-rw-r--r--synapse/media/v1/upload_resource.py2
9 files changed, 95 insertions, 24 deletions
diff --git a/synapse/media/v0/content_repository.py b/synapse/media/v0/content_repository.py
index ce5d3d153e..79ae0e3d74 100644
--- a/synapse/media/v0/content_repository.py
+++ b/synapse/media/v0/content_repository.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2014 OpenMarket Ltd
+# Copyright 2014, 2015 OpenMarket Ltd
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/synapse/media/v1/__init__.py b/synapse/media/v1/__init__.py
index e69de29bb2..d6c6690577 100644
--- a/synapse/media/v1/__init__.py
+++ b/synapse/media/v1/__init__.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+# Copyright 2014, 2015 OpenMarket Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import PIL.Image
+
+# check for JPEG support.
+try:
+    PIL.Image._getdecoder("rgb", "jpeg", None)
+except IOError as e:
+    if str(e).startswith("decoder jpeg not available"):
+        raise Exception(
+            "FATAL: jpeg codec not supported. Install pillow correctly! "
+            " 'sudo apt-get install libjpeg-dev' then 'pip uninstall pillow &&"
+            " pip install pillow --user'"
+        )
+except Exception:
+    # any other exception is fine
+    pass
+
+
+# check for PNG support.
+try:
+    PIL.Image._getdecoder("rgb", "zip", None)
+except IOError as e:
+    if str(e).startswith("decoder zip not available"):
+        raise Exception(
+            "FATAL: zip codec not supported. Install pillow correctly! "
+            " 'sudo apt-get install libjpeg-dev' then 'pip uninstall pillow &&"
+            " pip install pillow --user'"
+        )
+except Exception:
+    # any other exception is fine
+    pass
diff --git a/synapse/media/v1/base_resource.py b/synapse/media/v1/base_resource.py
index 2f5440ab64..688e7376ad 100644
--- a/synapse/media/v1/base_resource.py
+++ b/synapse/media/v1/base_resource.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2014 OpenMarket Ltd
+# Copyright 2014, 2015 OpenMarket Ltd
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -139,6 +139,7 @@ class BaseMediaResource(Resource):
             @download.addBoth
             def callback(media_info):
                 del self.downloads[key]
+                return media_info
         return download
 
     @defer.inlineCallbacks
@@ -201,7 +202,8 @@ class BaseMediaResource(Resource):
         defer.returnValue(media_info)
 
     @defer.inlineCallbacks
-    def _respond_with_file(self, request, media_type, file_path):
+    def _respond_with_file(self, request, media_type, file_path,
+                           file_size=None):
         logger.debug("Responding with %r", file_path)
 
         if os.path.isfile(file_path):
@@ -215,13 +217,20 @@ class BaseMediaResource(Resource):
             request.setHeader(
                 b"Cache-Control", b"public,max-age=86400,s-maxage=86400"
             )
+            if file_size is None:
+                stat = os.stat(file_path)
+                file_size = stat.st_size
+
+            request.setHeader(
+                b"Content-Length", b"%d" % (file_size,)
+            )
 
             with open(file_path, "rb") as f:
                 yield FileSender().beginFileTransfer(f, request)
 
             request.finish()
         else:
-            self._respond_404()
+            self._respond_404(request)
 
     def _get_thumbnail_requirements(self, media_type):
         if media_type == "image/jpeg":
diff --git a/synapse/media/v1/download_resource.py b/synapse/media/v1/download_resource.py
index f3a6804e05..c585bb11f7 100644
--- a/synapse/media/v1/download_resource.py
+++ b/synapse/media/v1/download_resource.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2014 OpenMarket Ltd
+# Copyright 2014, 2015 OpenMarket Ltd
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -46,23 +46,29 @@ class DownloadResource(BaseMediaResource):
     def _respond_local_file(self, request, media_id):
         media_info = yield self.store.get_local_media(media_id)
         if not media_info:
-            self._respond_404()
+            self._respond_404(request)
             return
 
         media_type = media_info["media_type"]
+        media_length = media_info["media_length"]
         file_path = self.filepaths.local_media_filepath(media_id)
 
-        yield self._respond_with_file(request, media_type, file_path)
+        yield self._respond_with_file(
+            request, media_type, file_path, media_length
+        )
 
     @defer.inlineCallbacks
     def _respond_remote_file(self, request, server_name, media_id):
         media_info = yield self._get_remote_media(server_name, media_id)
 
         media_type = media_info["media_type"]
+        media_length = media_info["media_length"]
         filesystem_id = media_info["filesystem_id"]
 
         file_path = self.filepaths.remote_media_filepath(
             server_name, filesystem_id
         )
 
-        yield self._respond_with_file(request, media_type, file_path)
+        yield self._respond_with_file(
+            request, media_type, file_path, media_length
+        )
diff --git a/synapse/media/v1/filepath.py b/synapse/media/v1/filepath.py
index 0078bc3d40..ed9a58e9d9 100644
--- a/synapse/media/v1/filepath.py
+++ b/synapse/media/v1/filepath.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2014 OpenMarket Ltd
+# Copyright 2014, 2015 OpenMarket Ltd
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/synapse/media/v1/media_repository.py b/synapse/media/v1/media_repository.py
index 2070ec3c7f..461cc001f1 100644
--- a/synapse/media/v1/media_repository.py
+++ b/synapse/media/v1/media_repository.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2014 OpenMarket Ltd
+# Copyright 2014, 2015 OpenMarket Ltd
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/synapse/media/v1/thumbnail_resource.py b/synapse/media/v1/thumbnail_resource.py
index e19620d456..84f5e3463c 100644
--- a/synapse/media/v1/thumbnail_resource.py
+++ b/synapse/media/v1/thumbnail_resource.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2014 OpenMarket Ltd
+# Copyright 2014, 2015 OpenMarket Ltd
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -100,11 +100,12 @@ class ThumbnailResource(BaseMediaResource):
             t_type = thumbnail_info["thumbnail_type"]
             t_method = thumbnail_info["thumbnail_method"]
             file_id = thumbnail_info["filesystem_id"]
+            t_length = thumbnail_info["thumbnail_length"]
 
             file_path = self.filepaths.remote_media_thumbnail(
                 server_name, file_id, t_width, t_height, t_type, t_method,
             )
-            yield self._respond_with_file(request, t_type, file_path)
+            yield self._respond_with_file(request, t_type, file_path, t_length)
         else:
             yield self._respond_default_thumbnail(
                 request, media_info, width, height, method, m_type,
@@ -139,11 +140,12 @@ class ThumbnailResource(BaseMediaResource):
         t_height = thumbnail_info["thumbnail_height"]
         t_type = thumbnail_info["thumbnail_type"]
         t_method = thumbnail_info["thumbnail_method"]
+        t_length = thumbnail_info["thumbnail_length"]
 
         file_path = self.filepaths.default_thumbnail(
             top_level_type, sub_type, t_width, t_height, t_type, t_method,
         )
-        yield self.respond_with_file(request, t_type, file_path)
+        yield self.respond_with_file(request, t_type, file_path, t_length)
 
     def _select_thumbnail(self, desired_width, desired_height, desired_method,
                           desired_type, thumbnail_infos):
@@ -165,18 +167,27 @@ class ThumbnailResource(BaseMediaResource):
                         aspect_quality, size_quality, type_quality,
                         length_quality, info
                     ))
-            return min(info_list)[-1]
+            if info_list:
+                return min(info_list)[-1]
         else:
             info_list = []
+            info_list2 = []
             for info in thumbnail_infos:
                 t_w = info["thumbnail_width"]
                 t_h = info["thumbnail_height"]
                 t_method = info["thumbnail_method"]
+                size_quality = abs((d_w - t_w) * (d_h - t_h))
+                type_quality = desired_type != info["thumbnail_type"]
+                length_quality = info["thumbnail_length"]
                 if t_method == "scale" and (t_w >= d_w or t_h >= d_h):
-                    size_quality = abs((d_w - t_w) * (d_h - t_h))
-                    type_quality = desired_type != info["thumbnail_type"]
-                    length_quality = info["thumbnail_length"]
                     info_list.append((
                         size_quality, type_quality, length_quality, info
                     ))
-            return min(info_list)[-1]
+                elif t_method == "scale":
+                    info_list2.append((
+                        size_quality, type_quality, length_quality, info
+                    ))
+            if info_list:
+                return min(info_list)[-1]
+            else:
+                return min(info_list2)[-1]
diff --git a/synapse/media/v1/thumbnailer.py b/synapse/media/v1/thumbnailer.py
index 774ae4538f..28404f2b7b 100644
--- a/synapse/media/v1/thumbnailer.py
+++ b/synapse/media/v1/thumbnailer.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2014 OpenMarket Ltd
+# Copyright 2014, 2015 OpenMarket Ltd
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -48,7 +48,7 @@ class Thumbnailer(object):
 
     def scale(self, output_path, width, height, output_type):
         """Rescales the image to the given dimensions"""
-        scaled = self.image.resize((width, height), Image.BILINEAR)
+        scaled = self.image.resize((width, height), Image.ANTIALIAS)
         return self.save_image(scaled, output_type, output_path)
 
     def crop(self, output_path, width, height, output_type):
@@ -65,7 +65,7 @@ class Thumbnailer(object):
         if width * self.height > height * self.width:
             scaled_height = (width * self.height) // self.width
             scaled_image = self.image.resize(
-                (width, scaled_height), Image.BILINEAR
+                (width, scaled_height), Image.ANTIALIAS
             )
             crop_top = (scaled_height - height) // 2
             crop_bottom = height + crop_top
@@ -73,7 +73,7 @@ class Thumbnailer(object):
         else:
             scaled_width = (height * self.width) // self.height
             scaled_image = self.image.resize(
-                (scaled_width, height), Image.BILINEAR
+                (scaled_width, height), Image.ANTIALIAS
             )
             crop_left = (scaled_width - width) // 2
             crop_right = width + crop_left
@@ -82,7 +82,7 @@ class Thumbnailer(object):
 
     def save_image(self, output_image, output_type, output_path):
         output_bytes_io = BytesIO()
-        output_image.save(output_bytes_io, self.FORMATS[output_type])
+        output_image.save(output_bytes_io, self.FORMATS[output_type], quality=70)
         output_bytes = output_bytes_io.getvalue()
         with open(output_path, "wb") as output_file:
             output_file.write(output_bytes)
diff --git a/synapse/media/v1/upload_resource.py b/synapse/media/v1/upload_resource.py
index 5645b0df46..b1718a630b 100644
--- a/synapse/media/v1/upload_resource.py
+++ b/synapse/media/v1/upload_resource.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright 2014 OpenMarket Ltd
+# Copyright 2014, 2015 OpenMarket Ltd
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.