Mercurial > silorider
changeset 67:c5bf03406a33
Resize photos below a file size threshold for all silos
Before we were doing it only for Bluesky, but now it's available to
all other silos.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Tue, 26 Dec 2023 16:30:36 -0800 |
parents | 4caf6720d1dd |
children | c678ca85cc25 |
files | silorider/commands/process.py silorider/silos/base.py silorider/silos/bluesky.py silorider/silos/facebook.py silorider/silos/twitter.py |
diffstat | 5 files changed, 45 insertions(+), 38 deletions(-) [+] |
line wrap: on
line diff
--- a/silorider/commands/process.py Tue Dec 26 16:27:28 2023 -0800 +++ b/silorider/commands/process.py Tue Dec 26 16:30:36 2023 -0800 @@ -124,7 +124,8 @@ media_callback = silo.mediaCallback if self.ctx.args.dry_run: media_callback = silo.dryRunMediaCallback - media_ids = upload_silo_media(entry_card, 'photo', media_callback) + max_size = getattr(silo, 'PHOTO_LIMIT', None) + media_ids = upload_silo_media(entry_card, 'photo', media_callback, max_size) if not self.ctx.args.dry_run: logger.debug("Posting to '%s': %s" % (silo.name, entry_url))
--- a/silorider/silos/base.py Tue Dec 26 16:27:28 2023 -0800 +++ b/silorider/silos/base.py Tue Dec 26 16:30:36 2023 -0800 @@ -5,6 +5,7 @@ import logging import tempfile import mimetypes +from PIL import Image from ..format import format_entry @@ -160,14 +161,14 @@ return silos -def upload_silo_media(card, propname, callback): +def upload_silo_media(card, propname, callback, max_size=None): # The provided callback must take the parameters: # tmpfile path, mimetype, original media url, media description with tempfile.TemporaryDirectory(prefix='SiloRider') as tmpdir: # Upload and use forced image, if any. if card.image: - mid = _do_upload_silo_media(tmpdir, card.image, None, callback) + mid = _do_upload_silo_media(tmpdir, card.image, None, callback, max_size) if mid is not None: return [mid] @@ -178,14 +179,14 @@ media_ids = [] for media_entry in media_entries: url, desc = _img_url_and_alt(media_entry) - mid = _do_upload_silo_media(tmpdir, url, desc, callback) + mid = _do_upload_silo_media(tmpdir, url, desc, callback, max_size) if mid is not None: media_ids.append(mid) return media_ids -def _do_upload_silo_media(tmpdir, url, desc, callback): +def _do_upload_silo_media(tmpdir, url, desc, callback, max_size=None): logger.debug("Downloading %s for upload to silo..." % url) mt, enc = mimetypes.guess_type(url, strict=False) if not mt: @@ -197,14 +198,48 @@ try: tmpfile = os.path.join(tmpdir, str(uuid.uuid4()) + ext) + logger.debug("Downloading photo to temporary file: %s" % tmpfile) tmpfile, headers = urllib.request.urlretrieve(url, filename=tmpfile) - logger.debug("Using temporary file: %s" % tmpfile) + tmpfile = _ensure_file_not_too_large(tmpfile, max_size) return callback(tmpfile, mt, url, desc) finally: logger.debug("Cleaning up.") urllib.request.urlcleanup() +def _ensure_file_not_too_large(path, max_size): + if max_size is None: + return path + + file_size = os.path.getsize(path) + if file_size <= max_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) + logger.debug("Now got file size %d (max size %d)" % (file_size, max_size)) + if file_size <= max_size: + return smaller_path + + scale = scale * scale + loops += 1 + + raise Exception("Can't reach a small enough image to upload!") + + def _img_url_and_alt(media_entry): # If an image has an alt attribute, the entry comes as a dictionary # with 'value' for the url and 'alt' for the description.
--- a/silorider/silos/bluesky.py Tue Dec 26 16:27:28 2023 -0800 +++ b/silorider/silos/bluesky.py Tue Dec 26 16:30:36 2023 -0800 @@ -12,8 +12,6 @@ import atproto import atproto.xrpc_client.models as atprotomodels -from PIL import Image - logger = logging.getLogger(__name__) @@ -52,9 +50,9 @@ class BlueskySilo(Silo): SILO_TYPE = 'bluesky' + PHOTO_LIMIT = 976560 _DEFAULT_SERVER = 'bsky.app' _CLIENT_CLASS = _BlueskyClient - _MAX_IMAGE_SIZE = 976560 def __init__(self, ctx): super().__init__(ctx) @@ -104,7 +102,6 @@ return card def mediaCallback(self, tmpfile, mt, url, desc): - tmpfile = self._ensureFileNotTooLarge(tmpfile) with open(tmpfile, 'rb') as tmpfp: data = tmpfp.read() @@ -116,34 +113,6 @@ 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