# HG changeset patch # User Ludovic Chabant # Date 1697582066 25200 # Node ID 76dce781334048525df42a58d455b560b4164846 # Parent d5ba2179d87604c36897f82724cae953c5738a4f Resize images if they're too large to be uploaded to Bluesky. This adds a new dependency to the Pillow library. diff -r d5ba2179d876 -r 76dce7813340 requirements.txt --- a/requirements.txt Tue Oct 17 15:32:23 2023 -0700 +++ b/requirements.txt Tue Oct 17 15:34:26 2023 -0700 @@ -15,6 +15,7 @@ mf2py==1.1.2 mf2util==0.5.2 oauthlib==3.2.2 +Pillow==10.0.1 python-dateutil==2.8.2 python-magic==0.4.27 pytz==2023.3 diff -r d5ba2179d876 -r 76dce7813340 setup.py --- a/setup.py Tue Oct 17 15:32:23 2023 -0700 +++ b/setup.py Tue Oct 17 15:34:26 2023 -0700 @@ -16,6 +16,7 @@ 'Mastodon.py>=1.3.0', 'mf2py>=1.1.0', 'mf2util>=0.5.0', + 'Pillow>=10.0.1', 'python-dateutil>=2.7.0', 'python-facebook-api>=0.17.1', 'python-twitter>=3.4.0', diff -r d5ba2179d876 -r 76dce7813340 silorider/silos/bluesky.py --- a/silorider/silos/bluesky.py Tue Oct 17 15:32:23 2023 -0700 +++ b/silorider/silos/bluesky.py Tue Oct 17 15:34:26 2023 -0700 @@ -1,4 +1,5 @@ import re +import os.path import json import time import urllib.parse @@ -11,6 +12,8 @@ import atproto import atproto.xrpc_client.models as atprotomodels +from PIL import Image + logger = logging.getLogger(__name__) @@ -51,6 +54,7 @@ SILO_TYPE = 'bluesky' _DEFAULT_SERVER = 'bsky.app' _CLIENT_CLASS = _BlueskyClient + _MAX_IMAGE_SIZE = 976560 def __init__(self, ctx): super().__init__(ctx) @@ -99,6 +103,7 @@ return card def mediaCallback(self, tmpfile, mt, url, desc): + tmpfile = self._ensureFileNotTooLarge(tmpfile) with open(tmpfile, 'rb') as tmpfp: data = tmpfp.read() @@ -110,6 +115,34 @@ desc = "" return atprotomodels.AppBskyEmbedImages.Image(alt=desc, image=upload.blob) + def _ensureFileNotTooLarge(self, path): + file_size = os.path.getsize(path) + if file_size <= self._MAX_IMAGE_SIZE: + return path + + loops = 0 + scale = 0.75 + path_no_ext, ext = os.path.splitext(path) + smaller_path = '%s_bsky%s' % (path_no_ext, ext) + with Image.open(path) as orig_im: + # Resize down 75% until we get below the size limit. + img_width, img_height = orig_im.size + while loops < 10: + logger.debug("Resizing '%s' by a factor of %f" % (path, scale)) + img_width = int(img_width * scale) + img_height = int(img_height * scale) + with orig_im.resize((img_width, img_height)) as smaller_im: + smaller_im.save(smaller_path) + + file_size = os.path.getsize(smaller_path) + if file_size <= self._MAX_IMAGE_SIZE: + return smaller_path + + scale = scale * scale + loops += 1 + + raise Exception("Can't reach a small enough image to upload!") + def postEntry(self, entry_card, media_ids, ctx): # Add images as an embed on the atproto record. embed = None