mamweb/galerie/typy.py
Pavel "LEdoian" Turinsky 289e09f558 Placeholdery, vol 1
2025-05-05 16:25:51 +02:00

140 lines
4.9 KiB
Python

"""
Naše typy objektů v galeriích.
V databázi jsou všechny v jedné tabulce, protože se liší jen prezentací navenek. Všechny implementují ~~rozhraní~~ ABC `ZobrazitelnyBazmek`.
Doporučené použití: TODO
"""
import abc
from datetime import datetime
import logging
logger = logging.getLogger(__name__)
from django.contrib.staticfiles.finders import find
# FIXME: static fily na lokálním webu??
find = lambda x: '/static/'+x
from django.utils.safestring import mark_safe, SafeString
from django.utils.html import format_html, format_html_join
from django.urls import reverse
from imagekit import ImageSpec
from imagekit.cachefiles import ImageCacheFile
from imagekit.processors import ResizeToFit, Transpose
HTML = str | SafeString
from galerie.models import Obrazek as DbObrazek
class ZobrazitelnyBazmek(abc.ABC):
def __init__(self, db_objekt):
self.db_objekt = db_objekt
# zkratka
self.soubor = db_objekt.soubor
# Volá se z templatetagu
@abc.abstractmethod
def zobrazit(self, **kwargs) -> HTML:
"""To, co se zobrazí v galerii jako hlavní obrázek (při prohlížení konkrétního obrázku a jako tittulní obrázek u galerií, které nemají vlastní obrázky (kupř. Vávrovka 2015))"""
...
@abc.abstractmethod
def zmenseny_nahled(self, **kwargs) -> HTML:
"""Zmenšené obrázky v přehledu obrázků a pod hlavním obrázkem (předchozí/následující)"""
...
@property
#@abc.abstractmethod
def cas_porizeni(self) -> datetime | None:
# TODO: použít tohle na automatické řazení věcí. Má vytáhnout datum z metadat
raise NotImplementedError("Prosím, naimplementuj hledání času vzniku v metadatech (nebo vrať None).")
#TODO: nativní rozměry?
### Obrázky ###
# Odpovídá původnímu chování (bo se mi nechce vymýšlet novoty…
class ObrazekStredniSpec(ImageSpec):
"""Specifikace obrázku pro velké zobrazení"""
processors = [
Transpose(Transpose.AUTO), # Rotuj podle dat v EXIFu
ResizeToFit(900, 675, upscale=False),
]
format = 'JPEG'
options = {'quality': 95} # Proč tolik?
class ObrazekMalySpec(ImageSpec):
"""Specifikace obrázku pro náhledy (pod hlavním obrázkem nebo v přehledu galerie)"""
processors = [
Transpose(Transpose.AUTO),
ResizeToFit(167, 167, upscale=False),
]
format = 'JPEG'
options = {'quality': 95}
def _fmt_attrs(attrs):
return format_html_join(' ', r'{}="{}"', ((mark_safe(k), v) for k, v in attrs.items()))
class Obrazek(ZobrazitelnyBazmek):
"""Obrázek pro zobrazení
Použije některý z ImageSpec-ů jako popis transformace a ImageCacheFile pro uložení výsledného obrázku.
Reference: https://django-imagekit.readthedocs.io/en/latest/#defining-specs-outside-of-models
Reference: https://django-imagekit.readthedocs.io/en/latest/caching.html
"""
def zobrazit(self, **kwargs):
# Jak se takový cachefile používá je potřeba vyčíst ze zdrojáků?
file = ImageCacheFile(ObrazekStredniSpec(source=self.soubor))
file.generate()
attrs = _fmt_attrs(kwargs)
html = format_html(r'<img src="{}" {} />', file.url, attrs)
return html
def zmenseny_nahled(self, **kwargs):
file = ImageCacheFile(ObrazekMalySpec(source=self.soubor))
file.generate()
attrs = _fmt_attrs(kwargs)
html = format_html(r'<img src="{}" {} />', file.url, attrs)
return html
class Video(ZobrazitelnyBazmek):
def __init__(self, *a, **kwa):
super().__init__(*a, **kwa)
self.placeholder = find('galerie/video_placeholder.svg')
def zobrazit(self, **kwargs):
attrs = _fmt_attrs(kwargs)
# Atributy specifické pro video musíme vesměs vyřešit tady… (šlo by to {% if %}-ovat podle typu, ale to je spíš haluz.
html = format_html(r'<video src="{}" preload="metadata" controls {} />', self.soubor.url, attrs)
return html
def zmenseny_nahled(self, **kwargs):
attrs = _fmt_attrs(kwargs)
return format_html(r'<img src="{}" {} />', self.placeholder, attrs)
class DummyBazmek(ZobrazitelnyBazmek):
def __init__(self, *a, **kwa):
super().__init__(*a, **kwa)
self.placeholder = find('galerie/neznamy_placeholder.svg')
def zobrazit(self, **kwargs):
attrs = _fmt_attrs(kwargs)
# Stavíme HTML ad-hoc, co se může rozbít :'-)
obrazek = format_html(r'<img src="{}"/>', self.placeholder, attrs)
popisek = format_html(r'<p style="text-align: center; font-size: 1.1em;">Prosím oprav typ aktuálního obrázku v <a href="{}">adminu</a>!</p>', reverse('admin:galerie_obrazek_change', args=(self.db_objekt.id,)))
return format_html(r'<div {}>{}{}</div>', attrs, obrazek, popisek)
def zmenseny_nahled(self, **kwargs):
attrs = _fmt_attrs(kwargs)
return format_html(r'<img src="{}" {} />', self.placeholder, attrs)
def tipniTyp(soubor) -> DbObrazek.Typ:
from PIL import Image, UnidentifiedImageError
try:
Image.open(soubor)
return DbObrazek.Typ.OBRAZEK
except UnidentifiedImageError:
return DbObrazek.Typ.NEVIM
logger.warning("Nepodařilo se tipnout typ nečekaným způsobem!")
return DbObrazek.Typ.NEVIM