mirror of
https://github.com/minetest/contentdb.git
synced 2025-01-05 20:57:28 +01:00
Use webp for thumbnails
This commit is contained in:
parent
5ce5684ca6
commit
4578cb157f
@ -426,7 +426,7 @@ def move_to_state(package):
|
|||||||
if not package.approved_at:
|
if not package.approved_at:
|
||||||
post_discord_webhook.delay(package.author.display_name,
|
post_discord_webhook.delay(package.author.display_name,
|
||||||
"New package {}".format(package.get_url("packages.view", absolute=True)), False,
|
"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()
|
package.approved_at = datetime.datetime.now()
|
||||||
|
|
||||||
screenshots = PackageScreenshot.query.filter_by(package=package, approved=False).all()
|
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:
|
elif state == PackageState.READY_FOR_REVIEW:
|
||||||
post_discord_webhook.delay(package.author.display_name,
|
post_discord_webhook.delay(package.author.display_name,
|
||||||
"Ready for Review: {}".format(package.get_url("packages.view", absolute=True)), True,
|
"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)
|
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
|
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,
|
post_discord_webhook.delay(current_user.username,
|
||||||
f"Deleted package {package.author.username}/{package.name} with reason '{reason}'",
|
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")
|
flash(gettext("Deleted package"), "success")
|
||||||
|
|
||||||
@ -500,7 +500,7 @@ def remove(package):
|
|||||||
|
|
||||||
post_discord_webhook.delay(current_user.username,
|
post_discord_webhook.delay(current_user.username,
|
||||||
"Unapproved package with reason {}\n\n{}".format(reason, package.get_url("packages.view", absolute=True)), True,
|
"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")
|
flash(gettext("Unapproved package"), "success")
|
||||||
|
|
||||||
|
@ -14,15 +14,17 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
from flask import abort, send_file, Blueprint, current_app
|
from flask import abort, send_file, Blueprint, current_app
|
||||||
|
|
||||||
bp = Blueprint("thumbnails", __name__)
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from PIL import Image
|
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):
|
def mkdir(path):
|
||||||
assert path != "" and path is not None
|
assert path != "" and path is not None
|
||||||
@ -34,10 +36,7 @@ def mkdir(path):
|
|||||||
|
|
||||||
|
|
||||||
def resize_and_crop(img_path, modified_path, size):
|
def resize_and_crop(img_path, modified_path, size):
|
||||||
try:
|
img = Image.open(img_path)
|
||||||
img = Image.open(img_path)
|
|
||||||
except FileNotFoundError:
|
|
||||||
abort(404)
|
|
||||||
|
|
||||||
# Get current and desired ratio for the images
|
# Get current and desired ratio for the images
|
||||||
img_ratio = img.size[0] / float(img.size[1])
|
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)
|
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>")
|
@bp.route("/thumbnails/<int:level>/<img>")
|
||||||
def make_thumbnail(img, level):
|
def make_thumbnail(img, level):
|
||||||
if level > len(ALLOWED_RESOLUTIONS) or level <= 0:
|
if level > len(ALLOWED_RESOLUTIONS) or level <= 0:
|
||||||
abort(403)
|
abort(403)
|
||||||
w, h = ALLOWED_RESOLUTIONS[level - 1]
|
w, h = ALLOWED_RESOLUTIONS[level - 1]
|
||||||
|
|
||||||
upload_dir = current_app.config["UPLOAD_DIR"]
|
|
||||||
thumbnail_dir = current_app.config["THUMBNAIL_DIR"]
|
thumbnail_dir = current_app.config["THUMBNAIL_DIR"]
|
||||||
mkdir(thumbnail_dir)
|
mkdir(thumbnail_dir)
|
||||||
|
|
||||||
output_dir = os.path.join(thumbnail_dir, str(level))
|
output_dir = os.path.join(thumbnail_dir, str(level))
|
||||||
mkdir(output_dir)
|
mkdir(output_dir)
|
||||||
|
|
||||||
cache_filepath = os.path.join(output_dir, img)
|
cache_filepath = os.path.join(output_dir, img)
|
||||||
source_filepath = os.path.join(upload_dir, img)
|
source_filepath = find_source_file(img)
|
||||||
|
|
||||||
resize_and_crop(source_filepath, cache_filepath, (w, h))
|
resize_and_crop(source_filepath, cache_filepath, (w, h))
|
||||||
return send_file(cache_filepath)
|
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):
|
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:
|
if release_id is None and no_load == False:
|
||||||
release = self.get_download_release(version=version)
|
release = self.get_download_release(version=version)
|
||||||
@ -555,7 +555,7 @@ class Package(db.Model):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def as_dict(self, base_url, version=None):
|
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)
|
release = self.get_download_release(version=version)
|
||||||
return {
|
return {
|
||||||
"author": self.author.username,
|
"author": self.author.username,
|
||||||
@ -603,21 +603,21 @@ class Package(db.Model):
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_thumb_or_placeholder(self, level=2):
|
def get_thumb_or_placeholder(self, level=2, format="webp"):
|
||||||
return self.get_thumb_url(level) or "/static/placeholder.png"
|
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
|
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:
|
if abs:
|
||||||
from app.utils import abs_url
|
from app.utils import abs_url
|
||||||
return abs_url(url)
|
return abs_url(url)
|
||||||
else:
|
else:
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def get_cover_image_url(self):
|
def get_cover_image_url(self, format="webp"):
|
||||||
screenshot = self.cover_image or self.main_screenshot
|
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):
|
def get_url(self, endpoint, absolute=False, **kwargs):
|
||||||
if absolute:
|
if absolute:
|
||||||
@ -1101,8 +1101,12 @@ class PackageScreenshot(db.Model):
|
|||||||
name=self.package.name,
|
name=self.package.name,
|
||||||
id=self.id)
|
id=self.id)
|
||||||
|
|
||||||
def get_thumb_url(self, level=2):
|
def get_thumb_url(self, level=2, format="webp"):
|
||||||
return self.url.replace("/uploads/", "/thumbnails/{:d}/".format(level))
|
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=""):
|
def as_dict(self, base_url=""):
|
||||||
return {
|
return {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
{%- endblock %}
|
{%- endblock %}
|
||||||
|
|
||||||
{% block headextra %}
|
{% 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 -%}
|
{% if thumb_url -%}
|
||||||
<meta name="og:image" content="{{ thumb_url }}">
|
<meta name="og:image" content="{{ thumb_url }}">
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{% macro render_pkgtile(package, show_author) -%}
|
{% macro render_pkgtile(package, show_author) -%}
|
||||||
<li class="packagetile flex-fill"><a href="{{ package.get_url('packages.view') }}">
|
<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="packagegridscrub"></div>
|
||||||
<div class="packagegridinfo">
|
<div class="packagegridinfo">
|
||||||
<h3>
|
<h3>
|
||||||
|
@ -10,8 +10,8 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block headextra %}
|
{% block headextra %}
|
||||||
{% if 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) }}"/>
|
<meta name="og:image" content="{{ package.get_thumb_url(3, True, "png") }}"/>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block headextra %}
|
{% block headextra %}
|
||||||
{% if 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) }}"/>
|
<meta name="og:image" content="{{ package.get_thumb_url(3, True, "png") }}"/>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block headextra %}
|
{% block headextra %}
|
||||||
{% if 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) }}"/>
|
<meta name="og:image" content="{{ package.get_thumb_url(3, True, "png") }}"/>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@ -261,7 +261,7 @@
|
|||||||
{% if ss.approved or package.check_perm(current_user, "ADD_SCREENSHOTS") %}
|
{% if ss.approved or package.check_perm(current_user, "ADD_SCREENSHOTS") %}
|
||||||
<div class="carousel-item {% if loop.index == 1 %}active{% endif %}">
|
<div class="carousel-item {% if loop.index == 1 %}active{% endif %}">
|
||||||
<a href="{{ ss.url }}" target="_blank">
|
<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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
Loading…
Reference in New Issue
Block a user