From 6004720c49b737d92901bf03d847c4bf24585ea6 Mon Sep 17 00:00:00 2001 From: Ryan Barrett Date: Mon, 6 Jan 2025 21:47:21 -0800 Subject: [PATCH] ATProto._convert: populate image embed aspect ratios for #1571 --- atproto.py | 9 ++++++--- tests/activitypub_logo.png | Bin 0 -> 8644 bytes tests/test_atproto.py | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 tests/activitypub_logo.png diff --git a/atproto.py b/atproto.py index d7a56bed..9c76f252 100644 --- a/atproto.py +++ b/atproto.py @@ -833,6 +833,8 @@ def _convert(cls, obj, fetch_blobs=False, from_user=None): return {} obj_as1 = obj.as1 + blobs = {} # maps str URL to dict blob object + aspect_ratios = {} # maps str URL to (int width, int height) tuple def fetch_blob(url, blob_field, name, check_size=True, check_type=True): if url and url not in blobs: @@ -843,10 +845,11 @@ def fetch_blob(url, blob_field, name, check_size=True, check_type=True): url=url, get_fn=util.requests_get, max_size=max_size, accept_types=accept) blobs[url] = blob.as_object() + if blob.width and blob.height: + aspect_ratios[url] = (blob.width, blob.height) except (RequestException, ValidationError) as e: logger.info(f'failed, skipping {url} : {e}') - blobs = {} # maps str URL to dict blob object if fetch_blobs: for o in obj.as1, as1.get_object(obj.as1): for url in util.get_urls(o, 'image'): @@ -878,8 +881,8 @@ def fetch_blob(url, blob_field, name, check_size=True, check_type=True): as_embed = obj.atom or obj.rss try: ret = bluesky.from_as1(cls.translate_ids(obj.as1), blobs=blobs, - client=client, original_fields_prefix='bridgy', - as_embed=as_embed) + aspects=aspect_ratios, client=client, + original_fields_prefix='bridgy', as_embed=as_embed) except (ValueError, RequestException): logger.info(f"Couldn't convert to ATProto", exc_info=True) return {} diff --git a/tests/activitypub_logo.png b/tests/activitypub_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d2ec7928f395a6553d86492a8fc759b4b4f00179 GIT binary patch literal 8644 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 literal 0 HcmV?d00001 diff --git a/tests/test_atproto.py b/tests/test_atproto.py index 6ad46bcc..681791ab 100644 --- a/tests/test_atproto.py +++ b/tests/test_atproto.py @@ -1,6 +1,7 @@ """Unit tests for atproto.py.""" import base64 import copy +from pathlib import Path from unittest import skip from unittest.mock import ANY, call, MagicMock, patch @@ -736,6 +737,38 @@ def test_convert_fetch_blobs_true_video_type_not_in_accept(self, mock_get): self.assertEqual(0, AtpRemoteBlob.query().count()) mock_get.assert_has_calls([self.req('https://my/vid')]) + @patch('requests.get', return_value=requests_response( + Path(__file__).with_name('activitypub_logo.png').read_bytes(), + content_type='image/png')) + def test_convert_fetch_blobs_true_image_aspect_ratio(self, mock_get): + cid = CID.decode('bafkreiefedbx7vctqxskqog5o3x2s4hnkchx7ml5aqwdbccgkzuis2ptya') + self.assertEqual({ + '$type': 'app.bsky.feed.post', + 'text': '', + 'createdAt': '2022-01-02T03:04:05.000Z', + 'embed': { + '$type': 'app.bsky.embed.images', + 'images': [{ + '$type': 'app.bsky.embed.images#image', + 'alt': '', + 'image': { + '$type': 'blob', + 'mimeType': 'image/png', + 'ref': cid, + 'size': 8644, + }, + 'aspectRatio': { + 'width': 260, + 'height': 164, + }, + }], + }, + }, ATProto.convert(Object(our_as1={ + 'objectType': 'note', + 'image': [{'url': 'http://my/pic/1'}], + }), fetch_blobs=True)) + mock_get.assert_has_calls([self.req('http://my/pic/1')]) + @patch('requests.get', side_effect=[ requests_response(status=404), requests_response('second blob contents', content_type='image/png')