diff options
author | Shay <hillerys@element.io> | 2024-07-08 02:11:20 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-08 10:11:20 +0100 |
commit | cf69f8d59b0a1fad2b0f313281647e3ea527cf5e (patch) | |
tree | 6542c9ad652b88d6653cf720cbbf9e3711942bdb /synapse/media/thumbnailer.py | |
parent | Bump ruff from 0.3.7 to 0.5.0 (#17381) (diff) | |
download | synapse-cf69f8d59b0a1fad2b0f313281647e3ea527cf5e.tar.xz |
Support MSC3916 by adding a federation /thumbnail endpoint and authenticated `_matrix/client/v1/media/thumbnail` endpoint (#17388)
[MSC3916](https://github.com/matrix-org/matrix-spec-proposals/pull/3916) added the endpoints `_matrix/federation/v1/media/thumbnail` and the authenticated `_matrix/client/v1/media/thumbnail`. This PR implements those endpoints, along with stabilizing `_matrix/client/v1/media/config` and `_matrix/client/v1/media/preview_url`. Complement tests are at https://github.com/matrix-org/complement/pull/728
Diffstat (limited to 'synapse/media/thumbnailer.py')
-rw-r--r-- | synapse/media/thumbnailer.py | 82 |
1 files changed, 61 insertions, 21 deletions
diff --git a/synapse/media/thumbnailer.py b/synapse/media/thumbnailer.py index f8a9560784..413a720e40 100644 --- a/synapse/media/thumbnailer.py +++ b/synapse/media/thumbnailer.py @@ -36,9 +36,11 @@ from synapse.media._base import ( ThumbnailInfo, respond_404, respond_with_file, + respond_with_multipart_responder, respond_with_responder, ) -from synapse.media.media_storage import MediaStorage +from synapse.media.media_storage import FileResponder, MediaStorage +from synapse.storage.databases.main.media_repository import LocalMedia if TYPE_CHECKING: from synapse.media.media_repository import MediaRepository @@ -271,6 +273,7 @@ class ThumbnailProvider: method: str, m_type: str, max_timeout_ms: int, + for_federation: bool, ) -> None: media_info = await self.media_repo.get_local_media_info( request, media_id, max_timeout_ms @@ -290,6 +293,8 @@ class ThumbnailProvider: media_id, url_cache=bool(media_info.url_cache), server_name=None, + for_federation=for_federation, + media_info=media_info, ) async def select_or_generate_local_thumbnail( @@ -301,6 +306,7 @@ class ThumbnailProvider: desired_method: str, desired_type: str, max_timeout_ms: int, + for_federation: bool, ) -> None: media_info = await self.media_repo.get_local_media_info( request, media_id, max_timeout_ms @@ -326,10 +332,16 @@ class ThumbnailProvider: responder = await self.media_storage.fetch_media(file_info) if responder: - await respond_with_responder( - request, responder, info.type, info.length - ) - return + if for_federation: + await respond_with_multipart_responder( + self.hs.get_clock(), request, responder, media_info + ) + return + else: + await respond_with_responder( + request, responder, info.type, info.length + ) + return logger.debug("We don't have a thumbnail of that size. Generating") @@ -344,7 +356,15 @@ class ThumbnailProvider: ) if file_path: - await respond_with_file(request, desired_type, file_path) + if for_federation: + await respond_with_multipart_responder( + self.hs.get_clock(), + request, + FileResponder(open(file_path, "rb")), + media_info, + ) + else: + await respond_with_file(request, desired_type, file_path) else: logger.warning("Failed to generate thumbnail") raise SynapseError(400, "Failed to generate thumbnail.") @@ -360,9 +380,10 @@ class ThumbnailProvider: desired_type: str, max_timeout_ms: int, ip_address: str, + use_federation: bool, ) -> None: media_info = await self.media_repo.get_remote_media_info( - server_name, media_id, max_timeout_ms, ip_address + server_name, media_id, max_timeout_ms, ip_address, use_federation ) if not media_info: respond_404(request) @@ -424,12 +445,13 @@ class ThumbnailProvider: m_type: str, max_timeout_ms: int, ip_address: str, + use_federation: bool, ) -> None: # TODO: Don't download the whole remote file # We should proxy the thumbnail from the remote server instead of # downloading the remote file and generating our own thumbnails. media_info = await self.media_repo.get_remote_media_info( - server_name, media_id, max_timeout_ms, ip_address + server_name, media_id, max_timeout_ms, ip_address, use_federation ) if not media_info: return @@ -448,6 +470,7 @@ class ThumbnailProvider: media_info.filesystem_id, url_cache=False, server_name=server_name, + for_federation=False, ) async def _select_and_respond_with_thumbnail( @@ -461,7 +484,9 @@ class ThumbnailProvider: media_id: str, file_id: str, url_cache: bool, + for_federation: bool, server_name: Optional[str] = None, + media_info: Optional[LocalMedia] = None, ) -> None: """ Respond to a request with an appropriate thumbnail from the previously generated thumbnails. @@ -476,6 +501,8 @@ class ThumbnailProvider: file_id: The ID of the media that a thumbnail is being requested for. url_cache: True if this is from a URL cache. server_name: The server name, if this is a remote thumbnail. + for_federation: whether the request is from the federation /thumbnail request + media_info: metadata about the media being requested. """ logger.debug( "_select_and_respond_with_thumbnail: media_id=%s desired=%sx%s (%s) thumbnail_infos=%s", @@ -511,13 +538,20 @@ class ThumbnailProvider: responder = await self.media_storage.fetch_media(file_info) if responder: - await respond_with_responder( - request, - responder, - file_info.thumbnail.type, - file_info.thumbnail.length, - ) - return + if for_federation: + assert media_info is not None + await respond_with_multipart_responder( + self.hs.get_clock(), request, responder, media_info + ) + return + else: + await respond_with_responder( + request, + responder, + file_info.thumbnail.type, + file_info.thumbnail.length, + ) + return # If we can't find the thumbnail we regenerate it. This can happen # if e.g. we've deleted the thumbnails but still have the original @@ -558,12 +592,18 @@ class ThumbnailProvider: ) responder = await self.media_storage.fetch_media(file_info) - await respond_with_responder( - request, - responder, - file_info.thumbnail.type, - file_info.thumbnail.length, - ) + if for_federation: + assert media_info is not None + await respond_with_multipart_responder( + self.hs.get_clock(), request, responder, media_info + ) + else: + await respond_with_responder( + request, + responder, + file_info.thumbnail.type, + file_info.thumbnail.length, + ) else: # This might be because: # 1. We can't create thumbnails for the given media (corrupted or |