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:
# thumbnails of existing images must exist in MEDIA_ROOT before
# the code expecting thumbnails of new dimensions can be deployed!
THUMBNAIL_SIZE_L = (1920, 1080)
THUMBNAIL_SIZE_S = (640, 360)
THUMBNAIL_SIZES = {'l': THUMBNAIL_SIZE_L, 's': THUMBNAIL_SIZE_S}
THUMBNAIL_SIZES = {'1080p': [1920, 1080], '360p': [640, 360]}
THUMBNAIL_FORMAT = 'PNG'
THUMBNAIL_QUALITY = 83

View File

@ -1,13 +1,13 @@
{% 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 %}">
{% 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 %}
<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>
<div class="ext-card-body">

View File

@ -3,15 +3,15 @@
{% if previews %}
<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 %}
{% with thumbnail_l_url=preview.thumbnail_l_url %}
{% with thumbnail_1080p_url=preview.thumbnail_1080p_url %}
<a
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 %}
data-galleria-content-type="{{ preview.content_type }}"
data-galleria-index="{{ forloop.counter }}">
<img src="{{ thumbnail_l_url }}" alt="{{ preview.preview.caption }}">
<img src="{{ thumbnail_1080p_url }}" alt="{{ preview.preview.caption }}">
</a>
{% endwith %}
{% endfor %}

View File

@ -7,7 +7,6 @@ import background_task.admin
import background_task.models
from .models import File, FileValidation
from constants.base import THUMBNAIL_SIZE_L
import files.signals
logger = logging.getLogger(__name__)
@ -47,11 +46,7 @@ class FileAdmin(admin.ModelAdmin):
if not obj or not (obj.is_image or obj.is_video):
return ''
try:
context = {
'file': obj,
'MEDIA_URL': settings.MEDIA_URL,
'THUMBNAIL_SIZE_L': THUMBNAIL_SIZE_L,
}
context = {'file': obj, 'MEDIA_URL': settings.MEDIA_URL}
return render_to_string('files/admin/thumbnails.html', context)
except Exception:
# 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
@property
def thumbnail_l_url(self) -> str:
return self.get_thumbnail_of_size('l')
def thumbnail_1080p_url(self) -> str:
return self.get_thumbnail_of_size('1080p')
@property
def thumbnail_s_url(self) -> str:
return self.get_thumbnail_of_size('s')
def thumbnail_360p_url(self) -> str:
return self.get_thumbnail_of_size('360p')
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)
if not thumbnail_field.name:
thumbnail_field.name = thumbnails['l']['path']
thumbnail_field.name = thumbnails['1080p']['path']
update_fields = set()
if thumbnail_field.name != unchanged_thumbnail:

View File

@ -1,16 +1,8 @@
<div class="file-thumbnails">
{% for size_key, thumb in file.metadata.thumbnails.items %}
{% for size_key, thumb in file.metadata.thumbnails.items %}
<div class="file-thumbnail">
<span class="file-thumbnail-size">{{ thumb.size.0 }}x{{ thumb.size.1 }}px</span>
<img height="{% widthratio thumb.size.1 10 1 %}" src="{{ MEDIA_URL }}{{ thumb.path }}" title={{ thumb.path }}>
</div>
{% 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 %}
{% endfor %}
</div>

View File

@ -53,8 +53,8 @@ class TasksTest(TestCase):
file.metadata,
{
'thumbnails': {
'l': {'path': 'thumbnails/fo/foobar_1920x1080.png', 'size': [1920, 1080]},
's': {'path': 'thumbnails/fo/foobar_640x360.png', 'size': [640, 360]},
'1080p': {'path': 'thumbnails/fo/foobar_1920x1080.png', 'size': [1920, 1080]},
'360p': {'path': 'thumbnails/fo/foobar_640x360.png', 'size': [640, 360]},
},
},
)
@ -86,8 +86,8 @@ class TasksTest(TestCase):
file.metadata,
{
'thumbnails': {
'l': {'path': 'thumbnails/de/deadbeef_1920x1080.png', 'size': [1920, 1080]},
's': {'path': 'thumbnails/de/deadbeef_640x360.png', 'size': [640, 360]},
'1080p': {'path': 'thumbnails/de/deadbeef_1920x1080.png', 'size': [1920, 1080]},
'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):
self.assertEqual(
{
'l': {'path': 'thumbnails/fo/foobar_1920x1080.png', 'size': (1920, 1080)},
's': {'path': 'thumbnails/fo/foobar_640x360.png', 'size': (640, 360)},
'1080p': {'path': 'thumbnails/fo/foobar_1920x1080.png', 'size': (1920, 1080)},
'360p': {'path': 'thumbnails/fo/foobar_640x360.png', 'size': (640, 360)},
},
make_thumbnails(TEST_FILES_DIR / 'test_preview_image_0001.png', 'foobar'),
)

View File

@ -76,10 +76,10 @@
<h3>Previews Pending Approval</h3>
<div class="row">
{% 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">
<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>
{% include "common/components/status.html" with object=preview.file class="d-block" %}
</div>