Thumbnails for images and videos #87

Merged
Anna Sirota merged 28 commits from thumbnails into main 2024-04-25 17:50:58 +02:00
10 changed files with 27 additions and 42 deletions
Showing only changes of commit f584f1a1bd - Show all commits

View File

@ -104,8 +104,6 @@ ABUSE_TYPE = Choices(
# **N.B.**: thumbnail sizes are not intended to be changed on the fly: # **N.B.**: thumbnail sizes are not intended to be changed on the fly:
# thumbnails of existing images must exist in MEDIA_ROOT before # thumbnails of existing images must exist in MEDIA_ROOT before
# the code expecting thumbnails of new dimensions can be deployed! # the code expecting thumbnails of new dimensions can be deployed!
THUMBNAIL_SIZE_L = (1920, 1080) THUMBNAIL_SIZES = {'1080p': [1920, 1080], '360p': [640, 360]}
THUMBNAIL_SIZE_S = (640, 360)
THUMBNAIL_SIZES = {'l': THUMBNAIL_SIZE_L, 's': THUMBNAIL_SIZE_S}
THUMBNAIL_FORMAT = 'PNG' THUMBNAIL_FORMAT = 'PNG'
THUMBNAIL_QUALITY = 83 THUMBNAIL_QUALITY = 83

View File

@ -1,13 +1,13 @@
{% load common filters %} {% load common filters %}
{% with latest=extension.latest_version thumbnail_s_url=extension.previews.listed.first.thumbnail_s_url %} {% with latest=extension.latest_version thumbnail_360p_url=extension.previews.listed.first.thumbnail_360p_url %}
<div class="ext-card {% if blur %}is-background-blur{% endif %}"> <div class="ext-card {% if blur %}is-background-blur{% endif %}">
{% if blur %} {% if blur %}
<div class="ext-card-thumbnail-blur" style="background-image: url({{ thumbnail_s_url }});"></div> <div class="ext-card-thumbnail-blur" style="background-image: url({{ thumbnail_360p_url }});"></div>
{% endif %} {% endif %}
<a class="ext-card-thumbnail" href="{{ extension.get_absolute_url }}"> <a class="ext-card-thumbnail" href="{{ extension.get_absolute_url }}">
<div class="ext-card-thumbnail-img" style="background-image: url({{ thumbnail_s_url }});" title="{{ extension.name }}"></div> <div class="ext-card-thumbnail-img" style="background-image: url({{ thumbnail_360p_url }});" title="{{ extension.name }}"></div>
</a> </a>
<div class="ext-card-body"> <div class="ext-card-body">

View File

@ -3,15 +3,15 @@
{% if previews %} {% if previews %}
<div class="galleria-items{% if previews.count > 5 %} is-many{% endif %}{% if previews.count == 1 %} is-single{% endif %}" id="galleria-items"> <div class="galleria-items{% if previews.count > 5 %} is-many{% endif %}{% if previews.count == 1 %} is-single{% endif %}" id="galleria-items">
{% for preview in previews %} {% for preview in previews %}
{% with thumbnail_l_url=preview.thumbnail_l_url %} {% with thumbnail_1080p_url=preview.thumbnail_1080p_url %}
<a <a
class="galleria-item js-galleria-item-preview galleria-item-type-{{ preview.content_type|slugify|slice:5 }}{% if forloop.first %} is-active{% endif %}" class="galleria-item js-galleria-item-preview galleria-item-type-{{ preview.content_type|slugify|slice:5 }}{% if forloop.first %} is-active{% endif %}"
href="{{ thumbnail_l_url }}" href="{{ thumbnail_1080p_url }}"
{% if 'video' in preview.content_type %}data-galleria-video-url="{{ preview.source.url }}"{% endif %} {% if 'video' in preview.content_type %}data-galleria-video-url="{{ preview.source.url }}"{% endif %}
data-galleria-content-type="{{ preview.content_type }}" data-galleria-content-type="{{ preview.content_type }}"
data-galleria-index="{{ forloop.counter }}"> data-galleria-index="{{ forloop.counter }}">
<img src="{{ thumbnail_l_url }}" alt="{{ preview.preview.caption }}"> <img src="{{ thumbnail_1080p_url }}" alt="{{ preview.preview.caption }}">
</a> </a>
{% endwith %} {% endwith %}
{% endfor %} {% endfor %}

View File

@ -7,7 +7,6 @@ import background_task.admin
import background_task.models import background_task.models
from .models import File, FileValidation from .models import File, FileValidation
from constants.base import THUMBNAIL_SIZE_L
import files.signals import files.signals
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -47,11 +46,7 @@ class FileAdmin(admin.ModelAdmin):
if not obj or not (obj.is_image or obj.is_video): if not obj or not (obj.is_image or obj.is_video):
return '' return ''
try: try:
context = { context = {'file': obj, 'MEDIA_URL': settings.MEDIA_URL}
'file': obj,
'MEDIA_URL': settings.MEDIA_URL,
'THUMBNAIL_SIZE_L': THUMBNAIL_SIZE_L,
}
return render_to_string('files/admin/thumbnails.html', context) return render_to_string('files/admin/thumbnails.html', context)
except Exception: except Exception:
# Make sure any exception happening here is always logged # Make sure any exception happening here is always logged

View File

@ -213,12 +213,12 @@ class File(CreatedModifiedMixin, TrackChangesMixin, models.Model):
return self.source.url return self.source.url
@property @property
def thumbnail_l_url(self) -> str: def thumbnail_1080p_url(self) -> str:
return self.get_thumbnail_of_size('l') return self.get_thumbnail_of_size('1080p')
@property @property
def thumbnail_s_url(self) -> str: def thumbnail_360p_url(self) -> str:
return self.get_thumbnail_of_size('s') return self.get_thumbnail_of_size('360p')
class FileValidation(CreatedModifiedMixin, TrackChangesMixin, models.Model): class FileValidation(CreatedModifiedMixin, TrackChangesMixin, models.Model):

View File

@ -56,7 +56,7 @@ def make_thumbnails(file_id: int) -> None:
thumbnails = files.utils.make_thumbnails(source_path, file.hash) thumbnails = files.utils.make_thumbnails(source_path, file.hash)
if not thumbnail_field.name: if not thumbnail_field.name:
thumbnail_field.name = thumbnails['l']['path'] thumbnail_field.name = thumbnails['1080p']['path']
update_fields = set() update_fields = set()
if thumbnail_field.name != unchanged_thumbnail: if thumbnail_field.name != unchanged_thumbnail:

View File

@ -5,12 +5,4 @@
<img height="{% widthratio thumb.size.1 10 1 %}" src="{{ MEDIA_URL }}{{ thumb.path }}" title={{ thumb.path }}> <img height="{% widthratio thumb.size.1 10 1 %}" src="{{ MEDIA_URL }}{{ thumb.path }}" title={{ thumb.path }}>
</div> </div>
{% endfor %} {% endfor %}
{# alert about unexpected size of the default thumbnail, which might happen if source file was smaller than 1080p #}
{# TODO: do we want to make minimum resolution a validation criteria during upload instead? #}
{% if file.thumbnail.width != THUMBNAIL_SIZE_L.0 or file.thumbnail.height != THUMBNAIL_SIZE_L.1 %}
<p>
<b class="icon-alert"></b> Expected {{ THUMBNAIL_SIZE_L.0 }}x{{ THUMBNAIL_SIZE_L.1 }}px,
got {{ file.thumbnail.width }}x{{ file.thumbnail.height }}px: resolution of the source file might be too low?
</p>
{% endif %}
</div> </div>

View File

@ -53,8 +53,8 @@ class TasksTest(TestCase):
file.metadata, file.metadata,
{ {
'thumbnails': { 'thumbnails': {
'l': {'path': 'thumbnails/fo/foobar_1920x1080.png', 'size': [1920, 1080]}, '1080p': {'path': 'thumbnails/fo/foobar_1920x1080.png', 'size': [1920, 1080]},
's': {'path': 'thumbnails/fo/foobar_640x360.png', 'size': [640, 360]}, '360p': {'path': 'thumbnails/fo/foobar_640x360.png', 'size': [640, 360]},
}, },
}, },
) )
@ -86,8 +86,8 @@ class TasksTest(TestCase):
file.metadata, file.metadata,
{ {
'thumbnails': { 'thumbnails': {
'l': {'path': 'thumbnails/de/deadbeef_1920x1080.png', 'size': [1920, 1080]}, '1080p': {'path': 'thumbnails/de/deadbeef_1920x1080.png', 'size': [1920, 1080]},
's': {'path': 'thumbnails/de/deadbeef_640x360.png', 'size': [640, 360]}, '360p': {'path': 'thumbnails/de/deadbeef_640x360.png', 'size': [640, 360]},
}, },
}, },
) )

View File

@ -126,8 +126,8 @@ class UtilsTest(TestCase):
def test_make_thumbnails(self, mock_resize_image): def test_make_thumbnails(self, mock_resize_image):
self.assertEqual( self.assertEqual(
{ {
'l': {'path': 'thumbnails/fo/foobar_1920x1080.png', 'size': (1920, 1080)}, '1080p': {'path': 'thumbnails/fo/foobar_1920x1080.png', 'size': (1920, 1080)},
's': {'path': 'thumbnails/fo/foobar_640x360.png', 'size': (640, 360)}, '360p': {'path': 'thumbnails/fo/foobar_640x360.png', 'size': (640, 360)},
}, },
make_thumbnails(TEST_FILES_DIR / 'test_preview_image_0001.png', 'foobar'), make_thumbnails(TEST_FILES_DIR / 'test_preview_image_0001.png', 'foobar'),
) )

View File

@ -76,10 +76,10 @@
<h3>Previews Pending Approval</h3> <h3>Previews Pending Approval</h3>
<div class="row"> <div class="row">
{% for preview in pending_previews %} {% for preview in pending_previews %}
{% with thumbnail_l_url=preview.file.thumbnail_l_url %} {% with thumbnail_1080p_url=preview.file.thumbnail_1080p_url %}
<div class="col-md-3"> <div class="col-md-3">
<a href="{{ preview.file.source.url }}" class="d-block mb-2" title="{{ preview.caption }}" target="_blank"> <a href="{{ preview.file.source.url }}" class="d-block mb-2" title="{{ preview.caption }}" target="_blank">
<img class="img-fluid rounded" src="{{ thumbnail_l_url }}" alt="{{ preview.caption }}"> <img class="img-fluid rounded" src="{{ thumbnail_1080p_url }}" alt="{{ preview.caption }}">
</a> </a>
{% include "common/components/status.html" with object=preview.file class="d-block" %} {% include "common/components/status.html" with object=preview.file class="d-block" %}
</div> </div>