Use webp for thumbnails

This commit is contained in:
rubenwardy 2023-11-10 18:57:49 +00:00
parent 5ce5684ca6
commit 4578cb157f
8 changed files with 57 additions and 35 deletions

@ -426,7 +426,7 @@ def move_to_state(package):
if not package.approved_at:
post_discord_webhook.delay(package.author.display_name,
"New package {}".format(package.get_url("packages.view", absolute=True)), False,
package.title, package.short_desc, package.get_thumb_url(2, True))
package.title, package.short_desc, package.get_thumb_url(2, True, "png"))
package.approved_at = datetime.datetime.now()
screenshots = PackageScreenshot.query.filter_by(package=package, approved=False).all()
@ -437,7 +437,7 @@ def move_to_state(package):
elif state == PackageState.READY_FOR_REVIEW:
post_discord_webhook.delay(package.author.display_name,
"Ready for Review: {}".format(package.get_url("packages.view", absolute=True)), True,
package.title, package.short_desc, package.get_thumb_url(2, True))
package.title, package.short_desc, package.get_thumb_url(2, True, "png"))
add_notification(package.maintainers, current_user, NotificationType.PACKAGE_APPROVAL, msg, package.get_url("packages.view"), package)
severity = AuditSeverity.NORMAL if current_user in package.maintainers else AuditSeverity.EDITOR
@ -480,7 +480,7 @@ def remove(package):
post_discord_webhook.delay(current_user.username,
f"Deleted package {package.author.username}/{package.name} with reason '{reason}'",
True, package.title, package.short_desc, package.get_thumb_url(2, True))
True, package.title, package.short_desc, package.get_thumb_url(2, True, "png"))
flash(gettext("Deleted package"), "success")
@ -500,7 +500,7 @@ def remove(package):
post_discord_webhook.delay(current_user.username,
"Unapproved package with reason {}\n\n{}".format(reason, package.get_url("packages.view", absolute=True)), True,
package.title, package.short_desc, package.get_thumb_url(2, True))
package.title, package.short_desc, package.get_thumb_url(2, True, "png"))
flash(gettext("Unapproved package"), "success")

@ -14,15 +14,17 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from flask import abort, send_file, Blueprint, current_app
bp = Blueprint("thumbnails", __name__)
import os
from PIL import Image
ALLOWED_RESOLUTIONS=[(100,67), (270,180), (350,233), (1100,520)]
bp = Blueprint("thumbnails", __name__)
ALLOWED_RESOLUTIONS = [(100, 67), (270, 180), (350, 233), (1100, 520)]
ALLOWED_EXTENSIONS = {"png", "webp"}
def mkdir(path):
assert path != "" and path is not None
@ -34,10 +36,7 @@ def mkdir(path):
def resize_and_crop(img_path, modified_path, size):
try:
img = Image.open(img_path)
except FileNotFoundError:
abort(404)
img = Image.open(img_path)
# Get current and desired ratio for the images
img_ratio = img.size[0] / float(img.size[1])
@ -64,21 +63,40 @@ def resize_and_crop(img_path, modified_path, size):
img.save(modified_path)
def find_source_file(img):
upload_dir = current_app.config["UPLOAD_DIR"]
source_filepath = os.path.join(upload_dir, img)
if os.path.isfile(source_filepath):
return source_filepath
period = source_filepath.rfind(".")
start = source_filepath[:period]
ext = source_filepath[period + 1:]
if ext not in ALLOWED_EXTENSIONS:
abort(404)
for other_ext in ALLOWED_EXTENSIONS:
other_path = f"{start}.{other_ext}"
if ext != other_ext and os.path.isfile(other_path):
return other_path
abort(404)
@bp.route("/thumbnails/<int:level>/<img>")
def make_thumbnail(img, level):
if level > len(ALLOWED_RESOLUTIONS) or level <= 0:
abort(403)
w, h = ALLOWED_RESOLUTIONS[level - 1]
upload_dir = current_app.config["UPLOAD_DIR"]
thumbnail_dir = current_app.config["THUMBNAIL_DIR"]
mkdir(thumbnail_dir)
output_dir = os.path.join(thumbnail_dir, str(level))
mkdir(output_dir)
cache_filepath = os.path.join(output_dir, img)
source_filepath = os.path.join(upload_dir, img)
cache_filepath = os.path.join(output_dir, img)
source_filepath = find_source_file(img)
resize_and_crop(source_filepath, cache_filepath, (w, h))
return send_file(cache_filepath)

@ -528,7 +528,7 @@ class Package(db.Model):
}
def as_short_dict(self, base_url, version=None, release_id=None, no_load=False):
tnurl = self.get_thumb_url(1)
tnurl = self.get_thumb_url(1, format="png")
if release_id is None and no_load == False:
release = self.get_download_release(version=version)
@ -555,7 +555,7 @@ class Package(db.Model):
return ret
def as_dict(self, base_url, version=None):
tnurl = self.get_thumb_url(1)
tnurl = self.get_thumb_url(1, format="png")
release = self.get_download_release(version=version)
return {
"author": self.author.username,
@ -603,21 +603,21 @@ class Package(db.Model):
]
}
def get_thumb_or_placeholder(self, level=2):
return self.get_thumb_url(level) or "/static/placeholder.png"
def get_thumb_or_placeholder(self, level=2, format="webp"):
return self.get_thumb_url(level, False, format) or "/static/placeholder.png"
def get_thumb_url(self, level=2, abs=False):
def get_thumb_url(self, level=2, abs=False, format="webp"):
screenshot = self.main_screenshot
url = screenshot.get_thumb_url(level) if screenshot is not None else None
url = screenshot.get_thumb_url(level, format) if screenshot is not None else None
if abs:
from app.utils import abs_url
return abs_url(url)
else:
return url
def get_cover_image_url(self):
def get_cover_image_url(self, format="webp"):
screenshot = self.cover_image or self.main_screenshot
return screenshot and screenshot.get_thumb_url(4)
return screenshot and screenshot.get_thumb_url(4, format)
def get_url(self, endpoint, absolute=False, **kwargs):
if absolute:
@ -1101,8 +1101,12 @@ class PackageScreenshot(db.Model):
name=self.package.name,
id=self.id)
def get_thumb_url(self, level=2):
return self.url.replace("/uploads/", "/thumbnails/{:d}/".format(level))
def get_thumb_url(self, level=2, format="webp"):
url = self.url.replace("/uploads/", "/thumbnails/{:d}/".format(level))
if format is not None:
start = url[:url.rfind(".")]
url = f"{start}.{format}"
return url
def as_dict(self, base_url=""):
return {

@ -9,7 +9,7 @@
{%- endblock %}
{% block headextra %}
{% set thumb_url = collection.packages and collection.packages[0].get_thumb_url(3, True) %}
{% set thumb_url = collection.packages and collection.packages[0].get_thumb_url(3, True, "png") %}
{% if thumb_url -%}
<meta name="og:image" content="{{ thumb_url }}">
{%- endif %}

@ -1,6 +1,6 @@
{% macro render_pkgtile(package, show_author) -%}
<li class="packagetile flex-fill"><a href="{{ package.get_url('packages.view') }}">
<img src="{{ package.get_thumb_or_placeholder(2) }}" loading="lazy">
<img src="{{ package.get_thumb_or_placeholder() }}" loading="lazy">
<div class="packagegridscrub"></div>
<div class="packagegridinfo">
<h3>

@ -10,8 +10,8 @@
{% endblock %}
{% block headextra %}
{% if package.get_thumb_url(3, True) %}
<meta name="og:image" content="{{ package.get_thumb_url(3, True) }}"/>
{% if package.get_thumb_url(3, True, "png") %}
<meta name="og:image" content="{{ package.get_thumb_url(3, True, "png") }}"/>
{% endif %}
{% endblock %}

@ -9,8 +9,8 @@
{% endblock %}
{% block headextra %}
{% if package.get_thumb_url(3, True) -%}
<meta name="og:image" content="{{ package.get_thumb_url(3, True) }}"/>
{% if package.get_thumb_url(3, True, "png") -%}
<meta name="og:image" content="{{ package.get_thumb_url(3, True, "png") }}"/>
{%- endif %}
{% endblock %}

@ -12,8 +12,8 @@
{% endblock %}
{% block headextra %}
{% if package.get_thumb_url(3, True) -%}
<meta name="og:image" content="{{ package.get_thumb_url(3, True) }}"/>
{% if package.get_thumb_url(3, True, "png") -%}
<meta name="og:image" content="{{ package.get_thumb_url(3, True, "png") }}"/>
{%- endif %}
{% endblock %}
@ -261,7 +261,7 @@
{% if ss.approved or package.check_perm(current_user, "ADD_SCREENSHOTS") %}
<div class="carousel-item {% if loop.index == 1 %}active{% endif %}">
<a href="{{ ss.url }}" target="_blank">
<img class="img-size w-100" src="{{ ss.url }}" alt="{{ ss.title }}" title="{{ ss.title }}" />
<img class="img-size w-100" loading="lazy" src="{{ ss.url }}" alt="{{ ss.title }}" title="{{ ss.title }}" />
</a>
</div>
{% endif %}