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/federation | |
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/federation')
-rw-r--r-- | synapse/federation/transport/server/__init__.py | 6 | ||||
-rw-r--r-- | synapse/federation/transport/server/_base.py | 4 | ||||
-rw-r--r-- | synapse/federation/transport/server/federation.py | 56 |
3 files changed, 65 insertions, 1 deletions
diff --git a/synapse/federation/transport/server/__init__.py b/synapse/federation/transport/server/__init__.py index c44e5daa47..5f997040d0 100644 --- a/synapse/federation/transport/server/__init__.py +++ b/synapse/federation/transport/server/__init__.py @@ -33,6 +33,7 @@ from synapse.federation.transport.server.federation import ( FEDERATION_SERVLET_CLASSES, FederationAccountStatusServlet, FederationMediaDownloadServlet, + FederationMediaThumbnailServlet, FederationUnstableClientKeysClaimServlet, ) from synapse.http.server import HttpServer, JsonResource @@ -316,7 +317,10 @@ def register_servlets( ): continue - if servletclass == FederationMediaDownloadServlet: + if ( + servletclass == FederationMediaDownloadServlet + or servletclass == FederationMediaThumbnailServlet + ): if not hs.config.server.enable_media_repo: continue diff --git a/synapse/federation/transport/server/_base.py b/synapse/federation/transport/server/_base.py index e124481474..9094201da0 100644 --- a/synapse/federation/transport/server/_base.py +++ b/synapse/federation/transport/server/_base.py @@ -363,6 +363,8 @@ class BaseFederationServlet: if ( func.__self__.__class__.__name__ # type: ignore == "FederationMediaDownloadServlet" + or func.__self__.__class__.__name__ # type: ignore + == "FederationMediaThumbnailServlet" ): response = await func( origin, content, request, *args, **kwargs @@ -375,6 +377,8 @@ class BaseFederationServlet: if ( func.__self__.__class__.__name__ # type: ignore == "FederationMediaDownloadServlet" + or func.__self__.__class__.__name__ # type: ignore + == "FederationMediaThumbnailServlet" ): response = await func( origin, content, request, *args, **kwargs diff --git a/synapse/federation/transport/server/federation.py b/synapse/federation/transport/server/federation.py index ec957768d4..b075a86f68 100644 --- a/synapse/federation/transport/server/federation.py +++ b/synapse/federation/transport/server/federation.py @@ -46,11 +46,13 @@ from synapse.http.servlet import ( parse_boolean_from_args, parse_integer, parse_integer_from_args, + parse_string, parse_string_from_args, parse_strings_from_args, ) from synapse.http.site import SynapseRequest from synapse.media._base import DEFAULT_MAX_TIMEOUT_MS, MAXIMUM_ALLOWED_MAX_TIMEOUT_MS +from synapse.media.thumbnailer import ThumbnailProvider from synapse.types import JsonDict from synapse.util import SYNAPSE_VERSION from synapse.util.ratelimitutils import FederationRateLimiter @@ -826,6 +828,59 @@ class FederationMediaDownloadServlet(BaseFederationServerServlet): ) +class FederationMediaThumbnailServlet(BaseFederationServerServlet): + """ + Implementation of new federation media `/thumbnail` endpoint outlined in MSC3916. Returns + a multipart/mixed response consisting of a JSON object and the requested media + item. This endpoint only returns local media. + """ + + PATH = "/media/thumbnail/(?P<media_id>[^/]*)" + RATELIMIT = True + + def __init__( + self, + hs: "HomeServer", + ratelimiter: FederationRateLimiter, + authenticator: Authenticator, + server_name: str, + ): + super().__init__(hs, authenticator, ratelimiter, server_name) + self.media_repo = self.hs.get_media_repository() + self.dynamic_thumbnails = hs.config.media.dynamic_thumbnails + self.thumbnail_provider = ThumbnailProvider( + hs, self.media_repo, self.media_repo.media_storage + ) + + async def on_GET( + self, + origin: Optional[str], + content: Literal[None], + request: SynapseRequest, + media_id: str, + ) -> None: + + width = parse_integer(request, "width", required=True) + height = parse_integer(request, "height", required=True) + method = parse_string(request, "method", "scale") + # TODO Parse the Accept header to get an prioritised list of thumbnail types. + m_type = "image/png" + max_timeout_ms = parse_integer( + request, "timeout_ms", default=DEFAULT_MAX_TIMEOUT_MS + ) + max_timeout_ms = min(max_timeout_ms, MAXIMUM_ALLOWED_MAX_TIMEOUT_MS) + + if self.dynamic_thumbnails: + await self.thumbnail_provider.select_or_generate_local_thumbnail( + request, media_id, width, height, method, m_type, max_timeout_ms, True + ) + else: + await self.thumbnail_provider.respond_local_thumbnail( + request, media_id, width, height, method, m_type, max_timeout_ms, True + ) + self.media_repo.mark_recently_accessed(None, media_id) + + FEDERATION_SERVLET_CLASSES: Tuple[Type[BaseFederationServlet], ...] = ( FederationSendServlet, FederationEventServlet, @@ -858,4 +913,5 @@ FEDERATION_SERVLET_CLASSES: Tuple[Type[BaseFederationServlet], ...] = ( FederationMakeKnockServlet, FederationAccountStatusServlet, FederationMediaDownloadServlet, + FederationMediaThumbnailServlet, ) |