download/thumbnail don't need authentification

This commit is contained in:
miruka 2019-11-12 09:10:00 -04:00
parent 4cc2ebf6e3
commit 73541ad7a5
18 changed files with 74 additions and 65 deletions

View File

@ -4,6 +4,7 @@
- Handle upload errors: non existent path, path is a dir, file too big, etc
- Show real progression for mxc thumbnail loadings, uploads and downloads
- Show reason under broken thumbnail icons
- Support m.file thumbnails
- Generate video thumbnails
- GIFs can use the video player

View File

@ -1,5 +1,6 @@
import asyncio
import logging as log
from pathlib import Path
from typing import Any, DefaultDict, Dict, List, Optional, Tuple, Union
import hsluv
@ -38,6 +39,10 @@ class Backend:
self.get_profile_locks: DefaultDict[str, asyncio.Lock] = \
DefaultDict(asyncio.Lock) # {user_id: lock}
from .media_cache import MediaCache
cache_dir = Path(self.app.appdirs.user_cache_dir)
self.media_cache = MediaCache(self, cache_dir)
def __repr__(self) -> str:
return f"{type(self).__name__}(clients={self.clients!r})"
@ -168,28 +173,6 @@ class Backend:
return (settings, ui_state, theme)
async def get_profile(self, user_id: str) -> nio.ProfileGetResponse:
if user_id in self.profile_cache:
return self.profile_cache[user_id]
async with self.get_profile_locks[user_id]:
while True:
try:
client = next(c for c in self.clients.values())
break
except StopIteration:
# Retry after a bit if no client was present yet
await asyncio.sleep(0.1)
response = await client.get_profile(user_id)
if isinstance(response, nio.ProfileGetError):
raise MatrixError.from_nio(response)
self.profile_cache[user_id] = response
return response
async def get_flat_sidepane_data(self) -> List[Dict[str, Any]]:
data = []
@ -210,3 +193,54 @@ class Backend:
})
return data
# Client functions that don't need authentification
async def _any_client(self) -> MatrixClient:
while True:
try:
return next(c for c in self.clients.values())
except StopIteration:
# Retry after a bit if we don't have any clients yet
await asyncio.sleep(0.1)
async def get_profile(self, user_id: str) -> nio.ProfileGetResponse:
if user_id in self.profile_cache:
return self.profile_cache[user_id]
async with self.get_profile_locks[user_id]:
response = await (await self._any_client()).get_profile(user_id)
if isinstance(response, nio.ProfileGetError):
raise MatrixError.from_nio(response)
self.profile_cache[user_id] = response
return response
async def thumbnail(
self, server_name: str, media_id: str, width: int, height: int,
) -> nio.ThumbnailResponse:
client = await self._any_client()
response = await client.thumbnail(server_name, media_id, width, height)
if isinstance(response, nio.ThumbnailError):
raise MatrixError.from_nio(response)
return response
async def download(
self, server_name: str, media_id: str,
) -> nio.DownloadResponse:
client = await self._any_client()
response = await client.download(server_name, media_id)
if isinstance(response, nio.DownloadError):
raise MatrixError.from_nio(response)
return response

View File

@ -80,10 +80,6 @@ class MatrixClient(nio.AsyncClient):
self.skipped_events: DefaultDict[str, int] = DefaultDict(lambda: 0)
from .media_cache import MediaCache
cache_dir = Path(self.backend.app.appdirs.user_cache_dir)
self.media_cache = MediaCache(self, cache_dir)
from .nio_callbacks import NioCallbacks
self.nio_callbacks = NioCallbacks(self)

View File

@ -12,7 +12,7 @@ from PIL import Image as PILImage
import nio
from .matrix_client import MatrixClient
from .backend import Backend
CryptDict = Optional[Dict[str, Any]]
Size = Tuple[int, int]
@ -21,12 +21,6 @@ CONCURRENT_DOWNLOADS_LIMIT = asyncio.BoundedSemaphore(8)
ACCESS_LOCKS: DefaultDict[str, asyncio.Lock] = DefaultDict(asyncio.Lock)
@dataclass
class DownloadFailed(Exception):
message: str = field()
http_code: int = field()
@dataclass
class Media:
cache: "MediaCache" = field()
@ -85,14 +79,11 @@ class Media:
async def _get_remote_data(self) -> bytes:
parsed = urlparse(self.mxc)
resp = await self.cache.client.download(
resp = await self.cache.backend.download(
server_name = parsed.netloc,
media_id = parsed.path.lstrip("/"),
)
if isinstance(resp, nio.DownloadError):
raise DownloadFailed(resp.message, resp.status_code)
return await self._decrypt(resp.body)
@ -181,21 +172,18 @@ class Thumbnail(Media):
parsed = urlparse(self.mxc)
if self.crypt_dict:
resp = await self.cache.client.download(
resp = await self.cache.backend.download(
server_name = parsed.netloc,
media_id = parsed.path.lstrip("/"),
)
else:
resp = await self.cache.client.thumbnail(
resp = await self.cache.backend.thumbnail(
server_name = parsed.netloc,
media_id = parsed.path.lstrip("/"),
width = self.wanted_size[0],
height = self.wanted_size[1],
)
if isinstance(resp, (nio.DownloadError, nio.ThumbnailError)):
raise DownloadFailed(resp.message, resp.status_code)
decrypted = await self._decrypt(resp.body)
with io.BytesIO(decrypted) as img:
@ -207,8 +195,8 @@ class Thumbnail(Media):
@dataclass
class MediaCache:
client: MatrixClient = field()
base_dir: Path = field()
backend: Backend = field()
base_dir: Path = field()
def __post_init__(self) -> None:

View File

@ -15,7 +15,6 @@ Rectangle {
theme.controls.avatar.background.opacity
)
property string clientUserId
property string name
property alias mxc: avatarImage.mxc
@ -53,7 +52,6 @@ Rectangle {
sourceSize.height: parent.height
fillMode: Image.PreserveAspectCrop
animate: false
clientUserId: avatar.clientUserId
HoverHandler { id: hoverHandler }
@ -74,7 +72,6 @@ Rectangle {
contentItem: HMxcImage {
id: avatarToolTipImage
fillMode: Image.PreserveAspectCrop
clientUserId: avatar.clientUserId
mxc: avatarImage.mxc
sourceSize.width: avatarToolTip.dimension

View File

@ -11,6 +11,7 @@ Image {
(sourceSize.width + sourceSize.height) <= 512
property bool broken: false
property bool animate: true
property bool animated: Utils.urlExtension(image.source) === "gif"
property alias progressBar: progressBar
@ -78,7 +79,7 @@ Image {
HIcon {
anchors.centerIn: parent
visible: image.status === Image.Error
visible: broken || image.status === Image.Error
svgName: "broken-image"
dimension: Math.max(16, Math.min(parent.width, parent.height) * 0.2)
colorize: theme.colors.negativeBackground

View File

@ -20,7 +20,6 @@ HImage {
}
property string clientUserId
property string mxc
property string sourceOverride: ""
property bool thumbnail: true
@ -53,12 +52,16 @@ HImage {
let args = image.thumbnail ?
[image.mxc, w, h, cryptDict] : [image.mxc, cryptDict]
py.callClientCoro(
clientUserId, "media_cache." + method, args, path => {
py.callCoro("media_cache." + method, args, path => {
if (! image) return
if (image.cachedPath != path) image.cachedPath = path
show = image.visible
}
image.broken = false
image.show = image.visible
}, () => {
image.broken = true
},
)
}
}

View File

@ -36,7 +36,6 @@ Rectangle {
HUserAvatar {
id: bannerAvatar
clientUserId: chatPage.userId
anchors.centerIn: parent
}
}

View File

@ -58,7 +58,6 @@ Rectangle {
HUserAvatar {
id: avatar
clientUserId: chatPage.userId
userId: writingUserId
displayName: writingUserInfo.display_name
mxc: writingUserInfo.avatar_url

View File

@ -23,7 +23,6 @@ Rectangle {
HRoomAvatar {
id: avatar
clientUserId: chatPage.userId
displayName: chatPage.roomInfo.display_name
mxc: chatPage.roomInfo.avatar_url
Layout.alignment: Qt.AlignTop

View File

@ -7,7 +7,6 @@ HTileDelegate {
backgroundColor: theme.chat.roomSidePane.member.background
image: HUserAvatar {
clientUserId: chatPage.userId
userId: model.user_id
displayName: model.display_name
mxc: model.avatar_url

View File

@ -57,7 +57,6 @@ HRowLayout {
HUserAvatar {
id: avatar
clientUserId: chatPage.userId
userId: model.sender_id
displayName: model.sender_name
mxc: model.sender_avatar

View File

@ -10,7 +10,6 @@ HMxcImage {
horizontalAlignment: Image.AlignLeft
animated: loader.singleMediaInfo.media_mime === "image/gif" ||
Utils.urlExtension(loader.mediaUrl) === "gif"
clientUserId: chatPage.userId
thumbnail: ! animated && loader.thumbnailMxc
mxc: thumbnail ?
(loader.thumbnailMxc || loader.mediaUrl) :

View File

@ -58,7 +58,6 @@ HGridLayout {
property bool changed: Boolean(sourceOverride)
id: avatar
clientUserId: accountSettings.userId
userId: accountSettings.userId
displayName: nameField.field.text
mxc: accountInfo.avatar_url

View File

@ -55,7 +55,6 @@ HBox {
HRoomAvatar {
id: avatar
clientUserId: userId
displayName: nameField.text
Layout.alignment: Qt.AlignCenter

View File

@ -2,8 +2,7 @@ import QtQuick 2.12
import "../../Base"
HUserAvatar {
clientUserId: addChatPage.userId
userId: clientUserId
userId: addChatPage.userId
displayName: addChatPage.account ? addChatPage.account.display_name : ""
mxc: addChatPage.account ? addChatPage.account.avatar_url : ""
}

View File

@ -46,7 +46,6 @@ HTileDelegate {
image: HUserAvatar {
clientUserId: model.data.user_id
userId: model.data.user_id
displayName: model.data.display_name
mxc: model.data.avatar_url

View File

@ -32,7 +32,6 @@ HTileDelegate {
image: HRoomAvatar {
clientUserId: model.user_id
displayName: model.data.display_name
mxc: model.data.avatar_url
}