From 899803467e5dc2c6677134a1919a6566b2e449e0 Mon Sep 17 00:00:00 2001 From: meeb Date: Sun, 6 Dec 2020 19:10:00 +1100 Subject: [PATCH] scale thumbnails properly --- app/common/static/images/nothumb.jpg | Bin 4644 -> 0 bytes app/common/static/images/nothumb.png | Bin 0 -> 4176 bytes app/sync/models.py | 10 +++++++--- app/sync/tasks.py | 16 ++++++---------- app/sync/templates/sync/media.html | 2 +- app/sync/utils.py | 22 ++++++++++++++++++++++ app/tubesync/settings.py | 1 + 7 files changed, 37 insertions(+), 14 deletions(-) delete mode 100644 app/common/static/images/nothumb.jpg create mode 100644 app/common/static/images/nothumb.png diff --git a/app/common/static/images/nothumb.jpg b/app/common/static/images/nothumb.jpg deleted file mode 100644 index c186a3c043d3c73c0fca31bfe78409523a231952..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4644 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 b$>ucW=+YSyR_T`=J@2x1s+T7te5dDM8FP-j diff --git a/app/common/static/images/nothumb.png b/app/common/static/images/nothumb.png new file mode 100644 index 0000000000000000000000000000000000000000..34dec031dcf389ce84d3ab45d7d5bc0cb251f69f GIT binary patch literal 4176 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 literal 0 HcmV?d00001 diff --git a/app/sync/models.py b/app/sync/models.py index 7fedb7f..34282cf 100644 --- a/app/sync/models.py +++ b/app/sync/models.py @@ -429,7 +429,11 @@ class Media(models.Model): def loaded_metadata(self): if self.pk in _metadata_cache: return _metadata_cache[self.pk] - _metadata_cache[self.pk] = json.loads(self.metadata) + try: + unpacked_data = json.loads(self.metadata) + except Exception: + return {} + _metadata_cache[self.pk] = unpacked_data return _metadata_cache[self.pk] @property @@ -460,9 +464,9 @@ class Media(models.Model): dateobj = upload_date if upload_date else self.created datestr = dateobj.strftime('%Y-%m-%d') source_name = slugify(self.source.name) - title = slugify(self.title.replace('&', 'and').replace('+', 'and')) + name = slugify(self.name.replace('&', 'and').replace('+', 'and')) ext = self.source.extension - fn = f'{datestr}_{source_name}_{title}'[:100] + fn = f'{datestr}_{source_name}_{name}'[:100] return f'{fn}.{ext}' @property diff --git a/app/sync/tasks.py b/app/sync/tasks.py index fd19244..b464145 100644 --- a/app/sync/tasks.py +++ b/app/sync/tasks.py @@ -15,7 +15,7 @@ from background_task import background from background_task.models import Task from common.logger import log from .models import Source, Media -from .utils import get_remote_image +from .utils import get_remote_image, resize_image_to_height def delete_index_source_task(source_id): @@ -57,10 +57,7 @@ def index_source_task(source_id): media.metadata = json.dumps(video) upload_date = media.upload_date if upload_date: - if timezone.is_aware(upload_date): - media.published = upload_date - else: - media.published = timezone.make_aware(upload_date) + media.published = timezone.make_aware(upload_date) media.save() log.info(f'Indexed media: {source} / {media}') @@ -76,13 +73,12 @@ def download_media_thumbnail(media_id, url): except Media.DoesNotExist: # Task triggered but the media no longer exists, ignore task return + width = getattr(settings, 'MEDIA_THUMBNAIL_WIDTH', 430) + height = getattr(settings, 'MEDIA_THUMBNAIL_HEIGHT', 240) i = get_remote_image(url) - ratio = i.width / i.height - new_height = getattr(settings, 'MEDIA_THUMBNAIL_HEIGHT', 240) - new_width = math.ceil(new_height * ratio) log.info(f'Resizing {i.width}x{i.height} thumbnail to ' - f'{new_width}x{new_height}: {url}') - i = i.resize((new_width, new_height), Image.ANTIALIAS) + f'{width}x{height}: {url}') + i = resize_image_to_height(i, width, height) image_file = BytesIO() i.save(image_file, 'JPEG', quality=80, optimize=True, progressive=True) image_file.seek(0) diff --git a/app/sync/templates/sync/media.html b/app/sync/templates/sync/media.html index 3a97bda..fa394b4 100644 --- a/app/sync/templates/sync/media.html +++ b/app/sync/templates/sync/media.html @@ -10,7 +10,7 @@
- + {{ m.source }}
{{ m.name }}
{{ m.published|date:'Y-m-d' }} diff --git a/app/sync/utils.py b/app/sync/utils.py index 0db29a6..a5c1cda 100644 --- a/app/sync/utils.py +++ b/app/sync/utils.py @@ -1,5 +1,6 @@ import os import re +import math from pathlib import Path import requests from PIL import Image @@ -59,6 +60,27 @@ def get_remote_image(url): return Image.open(r.raw) +def resize_image_to_height(image, width, height): + ''' + Resizes an image to 'height' pixels keeping the ratio. If the resulting width + is larger than 'width' then crop it. If the resulting width is smaller than + 'width' then stretch it. + ''' + ratio = image.width / image.height + scaled_width = math.ceil(height * ratio) + if scaled_width < width: + # Width too small, stretch it + scaled_width = width + image = image.resize((scaled_width, height), Image.ANTIALIAS) + if scaled_width > width: + # Width too large, crop it + delta = scaled_width - width + left, upper = (delta / 2), 0 + right, lower = (left + width), height + image = image.crop((left, upper, right, lower)) + return image + + def file_is_editable(filepath): ''' Checks that a file exists and the file is in an allowed predefined tuple of diff --git a/app/tubesync/settings.py b/app/tubesync/settings.py index 1a032e9..3ab4050 100644 --- a/app/tubesync/settings.py +++ b/app/tubesync/settings.py @@ -127,6 +127,7 @@ MEDIA_PER_PAGE = 36 INDEX_SOURCE_EVERY = 21600 # Seconds between indexing sources, 21600 = every 6 hours +MEDIA_THUMBNAIL_WIDTH = 430 # Width in pixels to resize thumbnails to MEDIA_THUMBNAIL_HEIGHT = 240 # Height in pixels to resize thumbnails to