wokring final build version

This commit is contained in:
2026-06-10 23:59:40 +02:00
parent 270d850572
commit 192143bfb6
61 changed files with 2184 additions and 639 deletions

View File

@@ -634,7 +634,7 @@ STATIC_ROOT = BASE_DIR / 'collectedstaticfiles'
if not USE_S3:
# Local filesystem — development only
STORAGES = {
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"default": {"BACKEND": "vontor_cz.storage.LocalMediaStorage"},
"staticfiles": {"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"},
}
MEDIA_URL = os.getenv("MEDIA_URL", "/media/")
@@ -650,8 +650,8 @@ else:
S3_PROTO = 'https' if S3_SSL else 'http'
STORAGES = {
"default": {"BACKEND": "storages.backends.s3boto3.S3Boto3Storage"},
"staticfiles": {"BACKEND": "storages.backends.s3boto3.S3StaticStorage"},
"default": {"BACKEND": "vontor_cz.storage.MediaStorage"},
"staticfiles": {"BACKEND": "vontor_cz.storage.StaticStorage"},
}
AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID')

View File

@@ -0,0 +1,82 @@
import io
import os
from django.core.files.base import ContentFile
from django.core.files.storage import FileSystemStorage
import pillow_heif
from PIL import Image, ImageSequence
pillow_heif.register_heif_opener()
from storages.backends.s3boto3 import S3Boto3Storage, S3StaticStorage
# All formats Pillow handles natively (no extra plugins needed).
# Add '.heic' / '.heif' if you install pillow-heif.
_IMAGE_EXTS = {
'.jpg', '.jpeg', '.jpe', '.jfif', # JPEG variants
'.png', # PNG
'.gif', # GIF (animated preserved)
'.bmp', '.dib', # BMP
'.tiff', '.tif', # TIFF
'.tga', # Truevision TGA
'.ico', # ICO (largest frame used)
'.ppm', '.pgm', '.pbm', '.pnm', # Portable pixmap family
'.pcx', # PCX
'.heic', '.heif', # Apple HEIC/HEIF (pillow-heif)
}
def _to_webp(content, quality: int = 85) -> io.BytesIO:
img = Image.open(content)
frames = list(ImageSequence.Iterator(img))
out = io.BytesIO()
if len(frames) > 1:
converted = [f.copy().convert('RGBA') for f in frames]
converted[0].save(
out,
format='WEBP',
save_all=True,
append_images=converted[1:],
loop=img.info.get('loop', 0),
quality=quality,
)
else:
img.convert('RGBA').save(out, format='WEBP', quality=quality)
out.seek(0)
return out
class WebPConversionMixin:
"""
Intercepts image/GIF uploads and converts them to WebP before storage.
Videos are saved as-is; use `vontor_cz.tasks.convert_video_to_webm` async
to transcode them to WebM after save.
"""
WEBP_QUALITY: int = 85
def _save(self, name: str, content) -> str:
root, ext = os.path.splitext(name)
if ext.lower() in _IMAGE_EXTS:
try:
webp_data = _to_webp(content, self.WEBP_QUALITY)
content = ContentFile(webp_data.read())
name = root + '.webp'
except Exception:
content.seek(0)
return super()._save(name, content)
class MediaStorage(WebPConversionMixin, S3Boto3Storage):
def exists(self, name):
if not name:
return False
return super().exists(name)
class LocalMediaStorage(WebPConversionMixin, FileSystemStorage):
pass
class StaticStorage(S3StaticStorage):
def exists(self, name):
if not name:
return False
return super().exists(name)