Fix SVG uploads, fix entire Upload model deleted
This commit is contained in:
		
							
								
								
									
										1
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								TODO.md
									
									
									
									
									
								
							| @@ -1,5 +1,4 @@ | ||||
| - Media | ||||
|   - SVG uploads | ||||
|   - Uploading progress bar (+local echo) | ||||
|   - Text bubbles theming | ||||
|   - Directly create cache files for our uploads before actually uploading | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| hsluv          == 0.0.2 | ||||
| Pillow         >= 5.4.1, < 6 | ||||
| pymediainfo    >= 4.1,   < 5 | ||||
| cairosvg       >= 2.4.2, < 3 | ||||
| aiofiles       >= 0.4.0, < 0.5 | ||||
| appdirs        >= 1.4.3, < 2 | ||||
| filetype       >= 1.0.5, < 2 | ||||
|   | ||||
| @@ -18,6 +18,7 @@ from typing import ( | ||||
| ) | ||||
| from uuid import uuid4 | ||||
|  | ||||
| import cairosvg | ||||
| from PIL import Image as PILImage | ||||
| from pymediainfo import MediaInfo | ||||
|  | ||||
| @@ -252,18 +253,22 @@ class MatrixClient(nio.AsyncClient): | ||||
|             content["url"] = url | ||||
|  | ||||
|         if kind == "image": | ||||
|             is_svg = mime == "image/svg+xml" | ||||
|  | ||||
|             event_type = \ | ||||
|                 nio.RoomEncryptedImage if encrypt else nio.RoomMessageImage | ||||
|  | ||||
|             content["msgtype"] = "m.image" | ||||
|  | ||||
|             content["info"]["w"], content["info"]["h"] = \ | ||||
|             content["info"]["w"], content["info"]["h"] = ( | ||||
|                 utils.svg_dimensions(str(path)) if is_svg else | ||||
|                 PILImage.open(path).size | ||||
|             ) | ||||
|  | ||||
|             try: | ||||
|                 thumb_url, thumb_info, thumb_crypt_dict = \ | ||||
|                     await self.upload_thumbnail( | ||||
|                         path, upload_item, encrypt=encrypt, | ||||
|                         path, upload_item, is_svg=is_svg, encrypt=encrypt, | ||||
|                     ) | ||||
|             except (UneededThumbnail, UnthumbnailableError): | ||||
|                 pass | ||||
| @@ -313,7 +318,7 @@ class MatrixClient(nio.AsyncClient): | ||||
|             content["filename"] = path.name | ||||
|  | ||||
|         upload_item.status = UploadStatus.Success | ||||
|         del self.models[Upload, room_id] | ||||
|         del self.models[Upload, room_id][upload_item.uuid] | ||||
|  | ||||
|         uuid = str(uuid4()) | ||||
|  | ||||
| @@ -451,19 +456,31 @@ class MatrixClient(nio.AsyncClient): | ||||
|         self, | ||||
|         path:    Union[Path, str], | ||||
|         item:    Optional[Upload] = None, | ||||
|         is_svg:  bool             = False, | ||||
|         encrypt: bool             = False, | ||||
|     ) -> Tuple[str, Dict[str, Any], CryptDict]: | ||||
|  | ||||
|         png_modes = ("1", "L", "P", "RGBA") | ||||
|  | ||||
|         try: | ||||
|             if is_svg: | ||||
|                 svg_width, svg_height = utils.svg_dimensions(str(path)) | ||||
|  | ||||
|                 thumb = PILImage.open(io.BytesIO( | ||||
|                     cairosvg.svg2png( | ||||
|                         url           = str(path), | ||||
|                         parent_width  = svg_width, | ||||
|                         parent_height = svg_height, | ||||
|                     ), | ||||
|                 )) | ||||
|             else: | ||||
|                 thumb = PILImage.open(path) | ||||
|  | ||||
|             small       = thumb.width <= 800 and thumb.height <= 600 | ||||
|             is_jpg_png  = thumb.format in ("JPEG", "PNG") | ||||
|             jpgable_png = thumb.format == "PNG" and thumb.mode not in png_modes | ||||
|  | ||||
|             if small and is_jpg_png and not jpgable_png: | ||||
|             if small and is_jpg_png and not jpgable_png and not is_svg: | ||||
|                 raise UneededThumbnail() | ||||
|  | ||||
|             if item: | ||||
|   | ||||
| @@ -3,7 +3,7 @@ import html | ||||
| import xml.etree.cElementTree as xml_etree  # FIXME: bandit warning | ||||
| from enum import Enum | ||||
| from enum import auto as autostr | ||||
| from typing import IO, Optional | ||||
| from typing import IO, Tuple, Union | ||||
|  | ||||
| import filetype | ||||
|  | ||||
| @@ -26,7 +26,7 @@ def dict_update_recursive(dict1, dict2): | ||||
|             dict1[k] = dict2[k] | ||||
|  | ||||
|  | ||||
| def is_svg(file: IO) -> bool: | ||||
| def is_svg(file: Union[IO, bytes, str]) -> bool: | ||||
|     try: | ||||
|         _, element = next(xml_etree.iterparse(file, ("start",))) | ||||
|         return element.tag == "{http://www.w3.org/2000/svg}svg" | ||||
| @@ -34,6 +34,22 @@ def is_svg(file: IO) -> bool: | ||||
|         return False | ||||
|  | ||||
|  | ||||
| def svg_dimensions(file: Union[IO, bytes, str]) -> Tuple[int, int]: | ||||
|     attrs = xml_etree.parse(file).getroot().attrib | ||||
|  | ||||
|     try: | ||||
|         width = round(float(attrs.get("width", attrs["viewBox"].split()[3]))) | ||||
|     except (KeyError, IndexError, ValueError, TypeError): | ||||
|         width = 256 | ||||
|  | ||||
|     try: | ||||
|         height = round(float(attrs.get("height", attrs["viewBox"].split()[4]))) | ||||
|     except (KeyError, IndexError, ValueError, TypeError): | ||||
|         height = 256 | ||||
|  | ||||
|     return (width, height) | ||||
|  | ||||
|  | ||||
| def guess_mime(file: IO) -> str: | ||||
|     if is_svg(file): | ||||
|         return "image/svg+xml" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	