Picture: surface the real reason a URL download fails
Couldn't reproduce the reported URL failing -- downloads fine from here (200, valid 2.5MB PNG, verified through the actual running app too) -- but found the error handling was swallowing the real exception either way: network failure and "not a valid image" both landed on the same generic message, and nothing was logged, so there was no way to tell which one it actually was, from the Pi or from here. Now: timeouts, connection/HTTP errors, and decode failures each get their own specific error message, and failures get logged via the standard logger (visible in journalctl -u paperwall.service). Also bumped the download timeout 15s -> 30s -- it's a one-time save-time download now, not a recurring poll, so there's no real cost to being more patient with a slow host or a large image. Verified against the real app: the exact reported URL, a non-image URL, and an unresolvable domain each now produce distinct, logged error messages instead of one generic one.
This commit is contained in:
1 parent
9aacd4936a
commit
474f8640fa
1 file changed
+20
-4
+20
-4
@@ -1,5 +1,6 @@
|
||||
"""Flask app: status/preview page + one settings form per screen + rotation settings."""
|
||||
|
||||
import logging
|
||||
from io import BytesIO
|
||||
|
||||
import requests
|
||||
@@ -9,9 +10,11 @@ from werkzeug.utils import secure_filename
|
||||
|
||||
from screens.picture import UPLOAD_DIR
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ALLOWED_IMAGE_EXTENSIONS = {"png", "jpg", "jpeg", "gif", "bmp", "webp"}
|
||||
PIL_FORMAT_TO_EXT = {"JPEG": "jpg", "PNG": "png", "GIF": "gif", "BMP": "bmp", "WEBP": "webp"}
|
||||
PICTURE_URL_TIMEOUT = 15
|
||||
PICTURE_URL_TIMEOUT = 30 # a one-time download at save-time, not a recurring poll -- fine to be generous
|
||||
MAX_SCHEDULE_RULES = 4
|
||||
PICTURE_URL_HEADERS = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
|
||||
@@ -244,12 +247,25 @@ def create_app(config, screens: dict, scheduler) -> Flask:
|
||||
response = requests.get(url, timeout=PICTURE_URL_TIMEOUT, headers=PICTURE_URL_HEADERS)
|
||||
response.raise_for_status()
|
||||
content = response.content
|
||||
fmt = Image.open(BytesIO(content)).format
|
||||
except (requests.exceptions.RequestException, OSError):
|
||||
fmt = None
|
||||
except requests.exceptions.Timeout:
|
||||
content = fmt = None
|
||||
error = f"Timed out downloading that URL after {PICTURE_URL_TIMEOUT}s -- the host might be slow or blocking this server."
|
||||
except requests.exceptions.RequestException as exc:
|
||||
content = fmt = None
|
||||
logger.warning("Picture URL download failed for %s: %s", url, exc)
|
||||
error = f"Could not download that URL -- {exc}"
|
||||
else:
|
||||
try:
|
||||
fmt = Image.open(BytesIO(content)).format
|
||||
except OSError as exc:
|
||||
logger.warning("Picture URL %s downloaded but didn't decode as an image: %s", url, exc)
|
||||
fmt = None
|
||||
|
||||
ext = PIL_FORMAT_TO_EXT.get(fmt or "")
|
||||
if not ext:
|
||||
error = "Could not download a valid image from that URL -- try a different link, or upload a file instead."
|
||||
if not error:
|
||||
error = "That URL doesn't look like a valid image -- try a different link, or upload a file instead."
|
||||
else:
|
||||
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
|
||||
# Only one picture is ever active -- clear any previous
|
||||
|
||||
Reference in new issue
Block a user