Support encrypting uploads
For files and thumbnails. Also fix the PIL thumbnail() bad argument function call.
This commit is contained in:
parent
b6609c5435
commit
ce2a7f1018
4
TODO.md
4
TODO.md
|
@ -8,8 +8,9 @@
|
||||||
- Deduplicate uploads
|
- Deduplicate uploads
|
||||||
- Encrypted media
|
- Encrypted media
|
||||||
- Loading progress bar
|
- Loading progress bar
|
||||||
- Support m.file thumbnails
|
|
||||||
|
|
||||||
|
- Support m.file thumbnails
|
||||||
|
- Generate video thumbnails
|
||||||
- GIFs can use the video player
|
- GIFs can use the video player
|
||||||
- Display GIF static thumbnails while the real GIF is loading
|
- Display GIF static thumbnails while the real GIF is loading
|
||||||
- Video bug: when media is done playing, clicking on progress slider always
|
- Video bug: when media is done playing, clicking on progress slider always
|
||||||
|
@ -35,6 +36,7 @@
|
||||||
- When qml syntax highlighting supports ES6 string interpolation, use that
|
- When qml syntax highlighting supports ES6 string interpolation, use that
|
||||||
|
|
||||||
- Fixes
|
- Fixes
|
||||||
|
- Restart sync is exception occurs
|
||||||
- In the "Leave me" room, "join > Hi > left" aren't combined
|
- In the "Leave me" room, "join > Hi > left" aren't combined
|
||||||
- Event delegates changing height don't scroll the list
|
- Event delegates changing height don't scroll the list
|
||||||
- When selecting text and scrolling up, selection stops working after a while
|
- When selecting text and scrolling up, selection stops working after a while
|
||||||
|
|
|
@ -11,10 +11,11 @@ from datetime import datetime
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import DefaultDict, Dict, Optional, Set, Tuple, Type, Union
|
from typing import Any, DefaultDict, Dict, Optional, Set, Tuple, Type, Union
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
import nio
|
import nio
|
||||||
|
from nio.crypto.attachments import encrypt_attachment
|
||||||
from PIL import Image as PILImage
|
from PIL import Image as PILImage
|
||||||
from pymediainfo import MediaInfo
|
from pymediainfo import MediaInfo
|
||||||
|
|
||||||
|
@ -213,18 +214,26 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
|
|
||||||
async def send_file(self, room_id: str, path: Union[Path, str]) -> None:
|
async def send_file(self, room_id: str, path: Union[Path, str]) -> None:
|
||||||
path = Path(path)
|
path = Path(path)
|
||||||
url, mime = await self.upload_file(path)
|
encrypt = room_id in self.encrypted_rooms
|
||||||
kind = (mime or "").split("/")[0]
|
|
||||||
|
url, mime, crypt_dict = await self.upload_file(path, encrypt=encrypt)
|
||||||
|
|
||||||
|
kind = (mime or "").split("/")[0]
|
||||||
|
|
||||||
content: dict = {
|
content: dict = {
|
||||||
"body": path.name,
|
"body": path.name,
|
||||||
"url": url,
|
|
||||||
"info": {
|
"info": {
|
||||||
"mimetype": mime,
|
"mimetype": mime,
|
||||||
"size": path.resolve().stat().st_size,
|
"size": path.resolve().stat().st_size,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if encrypt:
|
||||||
|
content["file"] = {"url": url, **crypt_dict}
|
||||||
|
else:
|
||||||
|
content["url"] = url
|
||||||
|
|
||||||
if kind == "image":
|
if kind == "image":
|
||||||
event_type = nio.RoomMessageImage
|
event_type = nio.RoomMessageImage
|
||||||
content["msgtype"] = "m.image"
|
content["msgtype"] = "m.image"
|
||||||
|
@ -233,11 +242,19 @@ class MatrixClient(nio.AsyncClient):
|
||||||
PILImage.open(path).size
|
PILImage.open(path).size
|
||||||
|
|
||||||
try:
|
try:
|
||||||
thumb_url, thumb_info = await self.upload_thumbnail(path)
|
thumb_url, thumb_info, thumb_crypt_dict = \
|
||||||
|
await self.upload_thumbnail(path, encrypt=encrypt)
|
||||||
except (UneededThumbnail, UnthumbnailableError):
|
except (UneededThumbnail, UnthumbnailableError):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
content["info"]["thumbnail_url"] = thumb_url
|
if encrypt:
|
||||||
|
content["info"]["thumbnail_file"] = {
|
||||||
|
"url": thumb_url,
|
||||||
|
**thumb_crypt_dict,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
content["info"]["thumbnail_url"] = thumb_url
|
||||||
|
|
||||||
content["info"]["thumbnail_info"] = thumb_info
|
content["info"]["thumbnail_info"] = thumb_info
|
||||||
|
|
||||||
elif kind == "audio":
|
elif kind == "audio":
|
||||||
|
@ -390,8 +407,9 @@ class MatrixClient(nio.AsyncClient):
|
||||||
self.models.pop((Member, room_id), None)
|
self.models.pop((Member, room_id), None)
|
||||||
|
|
||||||
|
|
||||||
async def upload_thumbnail(self, path: Union[Path, str],
|
async def upload_thumbnail(
|
||||||
) -> Tuple[str, Dict[str, Union[str, int]]]:
|
self, path: Union[Path, str], encrypt: bool = False,
|
||||||
|
) -> Tuple[str, Dict[str, Any], Dict[str, Any]]:
|
||||||
try:
|
try:
|
||||||
thumb = PILImage.open(path)
|
thumb = PILImage.open(path)
|
||||||
|
|
||||||
|
@ -403,7 +421,7 @@ class MatrixClient(nio.AsyncClient):
|
||||||
raise UneededThumbnail()
|
raise UneededThumbnail()
|
||||||
|
|
||||||
if not small:
|
if not small:
|
||||||
thumb.thumbnail((512, 512), filter=PILImage.LANCZOS)
|
thumb.thumbnail((512, 512), PILImage.LANCZOS)
|
||||||
|
|
||||||
with io.BytesIO() as out:
|
with io.BytesIO() as out:
|
||||||
if thumb.mode == "RGBA":
|
if thumb.mode == "RGBA":
|
||||||
|
@ -413,16 +431,24 @@ class MatrixClient(nio.AsyncClient):
|
||||||
thumb.convert("RGB").save(out, "JPEG", optimize=True)
|
thumb.convert("RGB").save(out, "JPEG", optimize=True)
|
||||||
mime = "image/jpeg"
|
mime = "image/jpeg"
|
||||||
|
|
||||||
content = out.getvalue()
|
data = out.getvalue()
|
||||||
|
|
||||||
|
if encrypt:
|
||||||
|
data, crypt_dict = encrypt_attachment(data)
|
||||||
|
upload_mime = "application/octet-stream"
|
||||||
|
else:
|
||||||
|
crypt_dict, upload_mime = {}, mime
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
await self.upload(content, mime, Path(path).name),
|
await self.upload(data, upload_mime, Path(path).name),
|
||||||
{
|
{
|
||||||
"w": thumb.width,
|
"w": thumb.width,
|
||||||
"h": thumb.height,
|
"h": thumb.height,
|
||||||
"mimetype": mime,
|
"mimetype": mime,
|
||||||
"size": len(content),
|
"size": len(data),
|
||||||
},
|
},
|
||||||
|
crypt_dict,
|
||||||
)
|
)
|
||||||
|
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
|
@ -430,12 +456,23 @@ class MatrixClient(nio.AsyncClient):
|
||||||
raise UnthumbnailableError(err)
|
raise UnthumbnailableError(err)
|
||||||
|
|
||||||
|
|
||||||
async def upload_file(self, path: Union[Path, str]) -> Tuple[str, str]:
|
async def upload_file(self, path: Union[Path, str], encrypt: bool = False,
|
||||||
|
) -> Tuple[str, str, Dict[str, Any]]:
|
||||||
with open(path, "rb") as file:
|
with open(path, "rb") as file:
|
||||||
mime = utils.guess_mime(file)
|
mime = utils.guess_mime(file)
|
||||||
file.seek(0, 0)
|
file.seek(0, 0)
|
||||||
|
|
||||||
return (await self.upload(file, mime, Path(path).name), mime)
|
if encrypt:
|
||||||
|
data, crypt_dict = encrypt_attachment(file.read())
|
||||||
|
upload_mime = "application/octet-stream"
|
||||||
|
else:
|
||||||
|
data, crypt_dict, upload_mime = file, {}, mime
|
||||||
|
|
||||||
|
return (
|
||||||
|
await self.upload(data, upload_mime, Path(path).name),
|
||||||
|
mime,
|
||||||
|
crypt_dict,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def upload(self, data, mime: str, filename: Optional[str] = None,
|
async def upload(self, data, mime: str, filename: Optional[str] = None,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user