Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
Switch to Photon for thumbnail generation (#1056)
Browse files Browse the repository at this point in the history
* Add env file for non-committable secrets

* Use Photon for upstream thumbnail compression

* Remove imaginary

* Remove accidental double encoding of forwarded params

* Add unit tests for photon util

* Add just env as a dependency to all recipes involving docker on CI

* Add Django secret key

* Fix lint errors

Co-authored-by: Madison Swain-Bowden <[email protected]>
  • Loading branch information
sarayourfriend and AetherUnbound authored Jan 9, 2023
1 parent 785594a commit d78abb9
Show file tree
Hide file tree
Showing 16 changed files with 429 additions and 646 deletions.
136 changes: 0 additions & 136 deletions .github/actions/staging-thumbnails-deploy/action.yml

This file was deleted.

106 changes: 0 additions & 106 deletions .github/workflows/build_imaginary.yml

This file was deleted.

2 changes: 1 addition & 1 deletion api/catalog/api/models/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class MediaMixin(models.Model):
)

# Because all forms of media have a thumbnail for visual representation
# For images, this field is not used as images are generated using ``imaginary``.
# For images, this field is not used as images are generated using Photon.
# For audio, this field points to the artwork, or is ``null``.
thumbnail = models.URLField(
max_length=1000,
Expand Down
104 changes: 104 additions & 0 deletions api/catalog/api/utils/photon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import logging
from urllib.parse import urlparse

from django.conf import settings
from django.http import HttpResponse
from rest_framework import status
from rest_framework.exceptions import APIException

import django_redis
import requests
import sentry_sdk


parent_logger = logging.getLogger(__name__)


class UpstreamThumbnailException(APIException):
status_code = status.HTTP_424_FAILED_DEPENDENCY
default_detail = "Could not render thumbnail due to upstream provider error."
default_code = "upstream_photon_failure"


HEADERS = {
"User-Agent": settings.OUTBOUND_USER_AGENT_TEMPLATE.format(
purpose="ThumbnailGeneration"
)
}


def get(
image_url: str,
accept_header: str = "image/*",
is_full_size: bool = False,
is_compressed: bool = True,
) -> HttpResponse:
logger = parent_logger.getChild("get")
# Photon options documented here:
# https://developer.wordpress.com/docs/photon/api/
params = {}

if not is_full_size:
params["w"] = settings.THUMBNAIL_WIDTH_PX

if is_compressed:
params["quality"] = settings.THUMBNAIL_QUALITY

parsed_image_url = urlparse(image_url)

if parsed_image_url.query:
# No need to URL encode this string because requests will already
# pass the `params` object to `urlencode` before it appends it to the
# request URL.
params["q"] = parsed_image_url.query

# Photon excludes the protocol so we need to reconstruct the url + port + path
# to send as the "path" of the Photon request
domain = parsed_image_url.netloc
path = parsed_image_url.path
upstream_url = f"{settings.PHOTON_ENDPOINT}{domain}{path}"

try:
headers = {"Accept": accept_header} | HEADERS
if settings.PHOTON_AUTH_KEY:
headers["X-Photon-Authentication"] = settings.PHOTON_AUTH_KEY

upstream_response = requests.get(
upstream_url,
timeout=10,
params=params,
headers=headers,
)
res_status = upstream_response.status_code
content_type = upstream_response.headers.get("Content-Type")
logger.debug(
"Image proxy response "
f"status: {res_status}, content-type: {content_type}"
)

return HttpResponse(
upstream_response.content,
status=res_status,
content_type=content_type,
)
except requests.ReadTimeout as exc:
# Count the incident so that we can identify providers with most timeouts.
key = f"{settings.THUMBNAIL_TIMEOUT_PREFIX}{domain}"
cache = django_redis.get_redis_connection("default")
try:
cache.incr(key)
except ValueError: # Key does not exist.
cache.set(key, 1)

sentry_sdk.capture_exception(exc)
raise UpstreamThumbnailException(
f"Failed to render thumbnail due to timeout: {exc}"
)
except requests.RequestException as exc:
sentry_sdk.capture_exception(exc)
raise UpstreamThumbnailException(f"Failed to render thumbnail: {exc}")
except Exception as exc:
sentry_sdk.capture_exception(exc)
raise UpstreamThumbnailException(
f"Failed to render thumbnail due to unidentified exception: {exc}"
)
Loading

0 comments on commit d78abb9

Please sign in to comment.