Thumbnails for images and videos #87
@ -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
|
||||||
|
@ -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">
|
||||||
|
@ -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 %}
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -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:
|
||||||
|
@ -1,16 +1,8 @@
|
|||||||
<div class="file-thumbnails">
|
<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">
|
<div class="file-thumbnail">
|
||||||
<span class="file-thumbnail-size">{{ thumb.size.0 }}x{{ thumb.size.1 }}px</span>
|
<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 }}>
|
<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>
|
||||||
|
@ -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]},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -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'),
|
||||||
)
|
)
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user