-
Notifications
You must be signed in to change notification settings - Fork 204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support MSC3916 by adding a federation /thumbnail endpoint and authenticated _matrix/client/v1/media/thumbnail
endpoint
#17388
Changes from all commits
91bc8f9
7c215c5
dcff123
7fed5d2
b1bffa5
63c129c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Support [MSC3916](https://github.com/matrix-org/matrix-spec-proposals/blob/rav/authentication-for-media/proposals/3916-authentication-for-media.md) | ||
by adding `_matrix/client/v1/media/thumbnail`, `_matrix/federation/v1/media/thumbnail` endpoints and stabilizing the | ||
remaining `_matrix/client/v1/media` endpoints. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,9 +36,11 @@ | |
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 @@ async def respond_local_thumbnail( | |
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 @@ async def respond_local_thumbnail( | |
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 @@ async def select_or_generate_local_thumbnail( | |
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 @@ async def select_or_generate_local_thumbnail( | |
|
||
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 @@ async def select_or_generate_local_thumbnail( | |
) | ||
|
||
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")), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we need to have any handling for if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like in the other places it's used there is no special handling - I can't think of what it would be other than an internal server error? |
||
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 @@ async def select_or_generate_remote_thumbnail( | |
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 @@ async def respond_remote_thumbnail( | |
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 @@ async def respond_remote_thumbnail( | |
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 @@ async def _select_and_respond_with_thumbnail( | |
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 @@ async def _select_and_respond_with_thumbnail( | |
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 @@ async def _select_and_respond_with_thumbnail( | |
|
||
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 @@ async def _select_and_respond_with_thumbnail( | |
) | ||
|
||
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 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is temporary (or at least looks it, given the TODO), but I am a little bit sceptical here at always using PNGs.
I would think that this will balloon the size of photographs for example. It's technically 'safe' but it wouldn't surprise me if the PNG thumbnail of a JPG photograph is bigger than the photograph.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to do a follow-up PR to address this - I don't want to tackle it in this PR because we're trying to get this in to the RC on tuesday.