Thumbnails for images and videos #87
@ -106,6 +106,6 @@ ABUSE_TYPE = Choices(
|
||||
# the code expecting thumbnails of new dimensions can be deployed!
|
||||
THUMBNAIL_SIZE_L = (1920, 1080)
|
||||
THUMBNAIL_SIZE_S = (640, 360)
|
||||
THUMBNAIL_SIZES = [THUMBNAIL_SIZE_L, THUMBNAIL_SIZE_S]
|
||||
THUMBNAIL_SIZES = {'l': THUMBNAIL_SIZE_L, 's': THUMBNAIL_SIZE_S}
|
||||
THUMBNAIL_FORMAT = 'PNG'
|
||||
THUMBNAIL_QUALITY = 83
|
||||
|
@ -199,7 +199,11 @@ class File(CreatedModifiedMixin, TrackChangesMixin, models.Model):
|
||||
|
||||
@property
|
||||
def thumbnail_l_url(self) -> str:
|
||||
"""Log absence of the thumbnail file instead of exploding somewhere in the templates."""
|
||||
"""Return absolute path portion of the URL of the large thumbnail of this file.
|
||||
|
||||
Fall back to the source file, if no thumbnail is stored.
|
||||
Log absence of the thumbnail file instead of exploding somewhere in the templates.
|
||||
"""
|
||||
try:
|
||||
return self.thumbnail.url
|
||||
except ValueError:
|
||||
@ -208,11 +212,16 @@ class File(CreatedModifiedMixin, TrackChangesMixin, models.Model):
|
||||
|
||||
@property
|
||||
def thumbnail_s_url(self) -> str:
|
||||
"""Log absence of the thumbnail file instead of exploding somewhere in the templates."""
|
||||
"""Return absolute path portion of the URL of the small thumbnail of this file.
|
||||
|
||||
Fall back to the source file, if no thumbnail is stored.
|
||||
Log absence of the thumbnail file instead of exploding somewhere in the templates.
|
||||
"""
|
||||
try:
|
||||
return self.metadata['thumbnails']['s']['path']
|
||||
except KeyError:
|
||||
log.exception(f'File pk={self.pk} is missing a small thumbnail')
|
||||
s = self.metadata['thumbnails']['s']['path']
|
||||
return self.thumbnail.storage.url(s)
|
||||
except (KeyError, TypeError):
|
||||
log.exception(f'File pk={self.pk} is missing a small thumbnail: {self.metadata}')
|
||||
return self.source.url
|
||||
|
||||
|
||||
|
@ -5,7 +5,6 @@ from background_task import background
|
||||
from background_task.tasks import TaskSchedule
|
||||
from django.conf import settings
|
||||
|
||||
from constants.base import THUMBNAIL_SIZE_L
|
||||
import files.models
|
||||
import files.utils
|
||||
|
||||
@ -32,7 +31,7 @@ def clamdscan(file_id: int):
|
||||
|
||||
@background(schedule={'action': TaskSchedule.RESCHEDULE_EXISTING})
|
||||
def make_thumbnails(file_id: int) -> None:
|
||||
"""Generate thumbnails for a given file."""
|
||||
"""Generate thumbnails for a given file, store them in thumbnail and metadata columns."""
|
||||
file = files.models.File.objects.get(pk=file_id)
|
||||
args = {'pk': file_id, 'type': file.get_type_display()}
|
||||
if not file.is_image and not file.is_video:
|
||||
@ -51,7 +50,7 @@ def make_thumbnails(file_id: int) -> None:
|
||||
source_path = output_path
|
||||
|
||||
thumbnails = files.utils.make_thumbnails(source_path, file.hash)
|
||||
thumbnail_default_path = next(_['path'] for _ in thumbnails if _['size'] == THUMBNAIL_SIZE_L)
|
||||
thumbnail_default_path = thumbnails['l']['path']
|
||||
|
||||
update_fields = set()
|
||||
if thumbnail_default_path != thumbnail_field.name:
|
||||
|
@ -1,10 +1,12 @@
|
||||
<div class="file-thumbnails">
|
||||
{% for thumb in file.metadata.thumbnails %}
|
||||
{% 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 resolusion 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,
|
||||
|
@ -227,14 +227,14 @@ def make_thumbnails(file_path: str, file_hash: str, output_format: str = THUMBNA
|
||||
"""Generate thumbnail files for given file and a predefined list of dimensions.
|
||||
|
||||
Resulting thumbnail paths a derived from the given file hash and thumbnail sizes.
|
||||
Return a list of sizes and output paths of generated thumbnail images.
|
||||
Return a dict of size keys to output paths of generated thumbnail images.
|
||||
"""
|
||||
thumbnail_ext = output_format.lower()
|
||||
if thumbnail_ext == 'jpeg':
|
||||
thumbnail_ext = 'jpg'
|
||||
base_path = get_base_path(get_thumbnail_upload_to(file_hash))
|
||||
thumbnails = []
|
||||
for w, h in THUMBNAIL_SIZES:
|
||||
thumbnails = {}
|
||||
for size_key, (w, h) in THUMBNAIL_SIZES.items():
|
||||
output_path = f'{base_path}_{w}x{h}.{thumbnail_ext}'
|
||||
size = (w, h)
|
||||
with tempfile.TemporaryFile() as f:
|
||||
@ -255,7 +255,7 @@ def make_thumbnails(file_path: str, file_hash: str, output_format: str = THUMBNA
|
||||
logger.warning('%s exists, overwriting', output_path)
|
||||
default_storage.delete(output_path)
|
||||
default_storage.save(output_path, f)
|
||||
thumbnails.append({'size': size, 'path': output_path})
|
||||
thumbnails[size_key] = {'size': size, 'path': output_path}
|
||||
return thumbnails
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user