Skip to content

Commit 11ede43

Browse files
committed
Download images parallel and use s3 directly
1 parent 4d869d5 commit 11ede43

File tree

2 files changed

+43
-18
lines changed

2 files changed

+43
-18
lines changed

appstore/api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,11 @@ def app_image_by_id(key):
7676
if hw in app.asset_collections:
7777
screenshot = app.asset_collections[hw].screenshots[0]
7878
if screenshot:
79-
screenshots[hw] = generate_image_url(screenshot, *plat_dimensions[hw], True)
79+
screenshots[hw] = screenshot
8080

8181
icon = None
8282
if app.type == 'watchapp':
83-
icon = generate_image_url(app.icon_large, 80, 80, True)
83+
icon = app.icon_large
8484
png = generate_preview_image(title=app.title, developer=app.developer.name, icon=icon, screenshots=screenshots)
8585
response = make_response(png)
8686
response.headers.set('Content-Type', 'image/png')

appstore/image.py

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import os
22
import io
3-
import requests
43

54
from flask import Flask
65
from PIL import Image, ImageDraw, ImageFont, ImageChops
6+
from concurrent.futures import ThreadPoolExecutor, as_completed
77
from math import ceil
88

9-
from .utils import valid_platforms
9+
from .s3 import download_asset
10+
from .utils import valid_platforms, plat_dimensions
1011

1112
parent_app = None
1213

@@ -69,16 +70,33 @@ def preferred_grouping(platforms):
6970
if len(selection) == len(selection & platforms):
7071
return selection
7172

72-
def load_image_from_url(url, fallback):
73+
def load_image_from_id(id, fallback):
7374
try:
74-
resp = requests.get(url, timeout=5)
75-
resp.raise_for_status()
76-
return Image.open(io.BytesIO(resp.content)).convert("RGBA")
75+
file = io.BytesIO()
76+
download_asset(id, file)
77+
return Image.open(file).convert("RGBA")
7778
except Exception as e:
7879
if fallback:
7980
return fallback
8081
raise e
8182

83+
def load_images_parallel(ids_with_fallbacks):
84+
output = {}
85+
with ThreadPoolExecutor(max_workers=5) as executor:
86+
future_to_key = {
87+
executor.submit(load_image_from_id, id, fallback): key
88+
for key, (id, fallback) in ids_with_fallbacks.items()
89+
}
90+
91+
for future in as_completed(future_to_key):
92+
key = future_to_key[future]
93+
try:
94+
output[key] = future.result()
95+
except Exception as e:
96+
output[key] = None
97+
98+
return output
99+
82100
def draw_text_ellipsized(draw, text, font, xy, max_width):
83101
if draw.textlength(text, font=font) <= max_width:
84102
draw.text(xy, text, font=font, fill=text_color)
@@ -96,17 +114,17 @@ def draw_text_ellipsized(draw, text, font, xy, max_width):
96114

97115
draw.text(xy, trimmed + ellipsis, font=font, fill=text_color)
98116

99-
def platform_image_in_border(canvas, image_url, top_left, platform):
117+
def platform_image_in_border(canvas, image, top_left, platform):
100118
border = platform_borders[platform]
101-
img = load_image_from_url(image_url, border['fallback'])
119+
image = image.resize(plat_dimensions[platform], resample=Image.NEAREST)
102120

103121
if platform == 'chalk':
104-
img.putalpha(chalk_mask)
122+
image.putalpha(chalk_mask)
105123

106124
ix = top_left[0] + border['offset'][0]
107125
iy = top_left[1] + border['offset'][1]
108126

109-
canvas.alpha_composite(img, (ix, iy))
127+
canvas.alpha_composite(image, (ix, iy))
110128

111129
canvas.alpha_composite(border['image'], top_left)
112130

@@ -117,10 +135,20 @@ def generate_preview_image(title, developer, icon, screenshots):
117135
platforms = preferred_grouping(screenshots.keys())
118136
start_x = ceil((canvas.width - sum(platform_borders[platform]['image'].width for platform in platforms)) / 2)
119137

138+
image_ids = {}
139+
140+
if icon:
141+
image_ids['icon'] = (icon, None)
142+
143+
for platform in platforms:
144+
image_ids[platform] = (screenshots[platform], platform_borders[platform]['fallback'])
145+
146+
loaded_images = load_images_parallel(image_ids)
147+
120148
for platform in platforms:
121149
platform_image_in_border(
122150
canvas=canvas,
123-
image_url=screenshots[platform],
151+
image=loaded_images[platform],
124152
top_left=(start_x, 0),
125153
platform=platform
126154
)
@@ -130,13 +158,10 @@ def generate_preview_image(title, developer, icon, screenshots):
130158
author_position = base_author_position
131159
text_space = base_text_space
132160

133-
icon_image = None
134-
try:
135-
icon_image = load_image_from_url(icon, None)
136-
except Exception as e:
137-
icon_image = None
161+
icon_image = loaded_images['icon'] if 'icon' in loaded_images else None
138162

139163
if icon_image:
164+
icon_image = icon_image.resize((80,80))
140165
icon_image.putalpha(ImageChops.multiply(icon_mask, icon_image.split()[3]))
141166
canvas.alpha_composite(icon_image, icon_position)
142167
title_position = (title_position[0] + 88, title_position[1])

0 commit comments

Comments
 (0)