Sprint 12+13: Tagebuch Day-One-Redesign, Notiz-Feature, Icon-Fixes, SW by-v405
Tagebuch:
- Day-One-Listenansicht: Wochentag + Tageszahl + Meta-Zeile (Zeit/Ort/Wetter)
- 4 Ansichten: Liste, Medien-Mosaik, Kalender (mit Sprungbuttons), Karte (GPS-Marker)
- Detail-Ansicht inline im Content-Bereich (kein Fullscreen-Overlay mehr)
- Hero-Bild vollständig sichtbar (object-fit:contain), Lightbox mit Safe-Area
- 2-Spalten-Layout Desktop: Text + Leaflet-Karte + POI-Liste
- EXIF-GPS-Extraktion bei Foto-Upload, historisches Wetter via Archive-API
- NoteStation-Import: Fotos in diary_media (80 Einträge migriert, 94 Medien)
- Stats-Endpoints: /diary/stats, /diary/calendar, /diary/locations
Notiz-Feature:
- Generische notes-Tabelle (parent_type + parent_id + meta_json)
- 📝-Button in 8 Bereichen, Notizblock-Seite mit KI-Analyse
- KI-Toggle in Einstellungen, notes_ki_enabled in User-Profil
Icons & Design:
- fill:currentColor Fix für welcome/onboarding/friends.js
- --c-icon Variable, --c-text-muted Dark Mode aufgehellt
- 15+ neue Phosphor-Icons aus lokaler Kopie
- CSS Network-First im SW, Cache-Control-Middleware
Infrastruktur:
- Wiki-Anreicherungs-Scheduler-Jobs entfernt (abgeschlossen)
- auth.py: notes_ki_enabled + is_social_media im User-Response
This commit is contained in:
parent
95f91fdc00
commit
553e9e7854
35 changed files with 4558 additions and 370 deletions
|
|
@ -117,6 +117,36 @@ def to_mp4_if_needed(data: bytes, filename: str) -> Tuple[bytes, str]:
|
|||
pass
|
||||
|
||||
|
||||
def extract_gps_from_exif(data: bytes) -> tuple | None:
|
||||
"""EXIF-GPS aus Bilddaten lesen. Gibt (lat, lon) zurück oder None."""
|
||||
try:
|
||||
from PIL import Image
|
||||
img = Image.open(io.BytesIO(data))
|
||||
exif = img._getexif()
|
||||
if not exif:
|
||||
return None
|
||||
gps = exif.get(34853) # GPSInfo tag
|
||||
if not gps:
|
||||
return None
|
||||
lat_dms = gps.get(2)
|
||||
lon_dms = gps.get(4)
|
||||
lat_ref = gps.get(1, 'N')
|
||||
lon_ref = gps.get(3, 'E')
|
||||
if not lat_dms or not lon_dms:
|
||||
return None
|
||||
|
||||
def dms(v):
|
||||
return float(v[0]) + float(v[1]) / 60 + float(v[2]) / 3600
|
||||
|
||||
lat = dms(lat_dms) * (-1 if lat_ref == 'S' else 1)
|
||||
lon = dms(lon_dms) * (-1 if lon_ref == 'W' else 1)
|
||||
if not (-90 <= lat <= 90 and -180 <= lon <= 180):
|
||||
return None
|
||||
return round(lat, 6), round(lon, 6)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def convert_media(data: bytes, filename: str) -> Tuple[bytes, str]:
|
||||
"""Convert HEIC→JPEG and MOV/AVI/M4V→MP4; pass everything else through."""
|
||||
ext = os.path.splitext(filename or "")[1].lower()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue