Fix image URL preview, add Thumbnail.create()

- Make HMxcImage's mxc property work with http too (temporary quick
solution)

- Thumbnail objects can now be initialized with existing bytes and not
  have to download anything.
This commit is contained in:
miruka 2019-11-04 07:00:28 -04:00
parent 337603595a
commit 484eefe86d
5 changed files with 44 additions and 39 deletions

View File

@ -443,7 +443,6 @@ class MatrixClient(nio.AsyncClient):
else: else:
crypt_dict, upload_mime = {}, mime crypt_dict, upload_mime = {}, mime
return ( return (
await self.upload(data, upload_mime, Path(path).name), await self.upload(data, upload_mime, Path(path).name),
{ {
@ -813,7 +812,7 @@ class MatrixClient(nio.AsyncClient):
ev, ev,
content = "", content = "",
inline_content = ev.body, inline_content = ev.body,
media_url = nio.Api.mxc_to_http(ev.url), media_url = ev.url,
media_title = ev.body, media_title = ev.body,
media_width = info.get("w") or 0, media_width = info.get("w") or 0,
media_height = info.get("h") or 0, media_height = info.get("h") or 0,
@ -821,9 +820,7 @@ class MatrixClient(nio.AsyncClient):
media_size = info.get("size") or 0, media_size = info.get("size") or 0,
media_mime = info.get("mimetype") or 0, media_mime = info.get("mimetype") or 0,
thumbnail_url = thumbnail_url = info.get("thumbnail_url") or "",
nio.Api.mxc_to_http(info.get("thumbnail_url") or ""),
thumbnail_width = thumb_info.get("w") or 0, thumbnail_width = thumb_info.get("w") or 0,
thumbnail_height = thumb_info.get("h") or 0, thumbnail_height = thumb_info.get("h") or 0,
) )

View File

@ -26,8 +26,9 @@ class DownloadFailed(Exception):
@dataclass @dataclass
class Media: class Media:
cache: "MediaCache" = field() cache: "MediaCache" = field()
mxc: str = field() mxc: str = field()
data: Optional[bytes] = field(repr=False)
def __post_init__(self) -> None: def __post_init__(self) -> None:
@ -54,7 +55,7 @@ class Media:
try: try:
return await self._get_local_existing_file() return await self._get_local_existing_file()
except FileNotFoundError: except FileNotFoundError:
return await self._download() return await self.create()
async def _get_local_existing_file(self) -> Path: async def _get_local_existing_file(self) -> Path:
@ -64,14 +65,15 @@ class Media:
return self.local_path return self.local_path
async def _download(self) -> Path: async def create(self) -> Path:
async with CONCURRENT_DOWNLOADS_LIMIT: if self.data is None:
body = await self._get_remote_data() async with CONCURRENT_DOWNLOADS_LIMIT:
self.data = await self._get_remote_data()
self.local_path.parent.mkdir(parents=True, exist_ok=True) self.local_path.parent.mkdir(parents=True, exist_ok=True)
async with aiofiles.open(self.local_path, "wb") as file: async with aiofiles.open(self.local_path, "wb") as file:
await file.write(body) await file.write(self.data)
return self.local_path return self.local_path
@ -82,9 +84,10 @@ class Media:
@dataclass @dataclass
class Thumbnail(Media): class Thumbnail(Media):
cache: "MediaCache" = field() cache: "MediaCache" = field()
mxc: str = field() mxc: str = field()
wanted_size: Size = field() data: Optional[bytes] = field(repr=False)
wanted_size: Size = field()
server_size: Optional[Size] = field(init=False, repr=False, default=None) server_size: Optional[Size] = field(init=False, repr=False, default=None)
@ -143,7 +146,6 @@ class Thumbnail(Media):
raise FileNotFoundError() raise FileNotFoundError()
async def _get_remote_data(self) -> bytes: async def _get_remote_data(self) -> bytes:
parsed = urlparse(self.mxc) parsed = urlparse(self.mxc)
@ -154,13 +156,13 @@ class Thumbnail(Media):
height = self.wanted_size[1], height = self.wanted_size[1],
) )
if isinstance(resp, nio.ThumbnailError):
raise DownloadFailed(resp.message, resp.status_code)
with io.BytesIO(resp.body) as img: with io.BytesIO(resp.body) as img:
# The server may return a thumbnail bigger than what we asked for # The server may return a thumbnail bigger than what we asked for
self.server_size = PILImage.open(img).size self.server_size = PILImage.open(img).size
if isinstance(resp, nio.ErrorResponse):
raise DownloadFailed(resp.message, resp.status_code)
return resp.body return resp.body
@ -178,5 +180,5 @@ class MediaCache:
self.downloads_dir.mkdir(parents=True, exist_ok=True) self.downloads_dir.mkdir(parents=True, exist_ok=True)
async def thumbnail(self, mxc: str, width: int, height: int) -> str: async def get_thumbnail(self, mxc: str, width: int, height: int) -> str:
return str(await Thumbnail(self, mxc, (width, height)).get()) return str(await Thumbnail(self, mxc, None, (width, height)).get())

View File

@ -31,10 +31,18 @@ HImage {
if (! image) return // if it was destroyed if (! image) return // if it was destroyed
py.callClientCoro(clientUserId, "media_cache.thumbnail", arg, path => { if (! image.mxc.startsWith("mxc://")) {
if (! image) return source = mxc
image.cachedPath = path show = image.visible
show = image.visible return
}) }
py.callClientCoro(
clientUserId, "media_cache.get_thumbnail", [mxc, w, h], path => {
if (! image) return
image.cachedPath = path
show = image.visible
}
)
} }
} }

View File

@ -2,17 +2,19 @@ import QtQuick 2.12
import "../../Base" import "../../Base"
import "../../utils.js" as Utils import "../../utils.js" as Utils
HImage { HMxcImage {
id: image id: image
horizontalAlignment: Image.AlignLeft horizontalAlignment: Image.AlignLeft
sourceSize.width: theme.chat.message.thumbnailWidth // FIXME sourceSize.width: 640 // FIXME
source: animated ? openUrl : loader.previewUrl sourceSize.height: 480 // FIXME
animated: loader.singleMediaInfo.media_mime === "image/gif" || animated: loader.singleMediaInfo.media_mime === "image/gif" ||
Utils.urlExtension(loader.mediaUrl) === "gif" Utils.urlExtension(loader.mediaUrl) === "gif"
clientUserId: chatPage.userId
mxc: animated ? openUrl : (loader.thumbnailMxc || loader.mediaUrl)
property EventMediaLoader loader property EventMediaLoader loader
readonly property url openUrl: loader.mediaUrl || loader.previewUrl readonly property url openUrl: loader.mediaUrl || loader.thumbnailMxc
TapHandler { TapHandler {

View File

@ -8,7 +8,7 @@ HLoader {
property QtObject singleMediaInfo property QtObject singleMediaInfo
property url mediaUrl property string mediaUrl
property string showSender: "" property string showSender: ""
property string showDate: "" property string showDate: ""
@ -47,21 +47,17 @@ HLoader {
return EventDelegate.Media.Page return EventDelegate.Media.Page
} }
readonly property url previewUrl: ( readonly property string thumbnailMxc: singleMediaInfo.thumbnail_url
type === EventDelegate.Media.File ||
type === EventDelegate.Media.Image ?
singleMediaInfo.thumbnail_url : ""
) || mediaUrl
onPreviewUrlChanged: { onTypeChanged: {
if (type === EventDelegate.Media.Image) { if (type === EventDelegate.Media.Image) {
var file = "EventImage.qml" var file = "EventImage.qml"
// } else if (type === EventDelegate.Media.File) { // } else if (type === EventDelegate.Media.File) {
// var file = "EventFile.qml" // var file = "EventFile.qml"
// var props = { // var props = {
// thumbnailUrl: previewUrl, // thumbnailMxc: thumbnailMxc,
// fileUrl: mediaUrl, // fileUrl: mediaUrl,
// fileTitle: info.media_title, // fileTitle: info.media_title,
// fileSize: info.media_size, // fileSize: info.media_size,