Feature: Tagebuch Cover-Bild (Favorit-Funktion) für diary_media
- Migration: diary_media.is_cover (INTEGER DEFAULT 0)
- Upload: erstes Item eines Eintrags automatisch is_cover=1
- Neuer Endpoint: PATCH /diary/{id}/media/{mid}/cover
- GET-Endpoints geben is_cover + cover_url zurück
- Frontend: Stern-Button (⭐) in Gallery-Detail und Edit-Formular
- Timeline-Karte verwendet cover_url als Vorschaubild
- SW by-v212, APP_VER 186
This commit is contained in:
parent
63ab092f5e
commit
fa0fcbf8c9
7 changed files with 196 additions and 21 deletions
|
|
@ -87,12 +87,12 @@ def _set_dog_ids(conn, entry_id: int, dog_ids: list[int]):
|
|||
|
||||
|
||||
def _fetch_media_items(conn, entry_ids: list[int]) -> dict:
|
||||
"""Gibt {entry_id: [{url, media_type, sort_order, id}, ...]} zurück."""
|
||||
"""Gibt {entry_id: [{url, media_type, sort_order, id, is_cover}, ...]} zurück."""
|
||||
if not entry_ids:
|
||||
return {}
|
||||
ph = ",".join("?" * len(entry_ids))
|
||||
rows = conn.execute(
|
||||
f"SELECT id, diary_id, url, media_type, sort_order FROM diary_media "
|
||||
f"SELECT id, diary_id, url, media_type, sort_order, is_cover FROM diary_media "
|
||||
f"WHERE diary_id IN ({ph}) ORDER BY diary_id, sort_order",
|
||||
entry_ids
|
||||
).fetchall()
|
||||
|
|
@ -100,7 +100,8 @@ def _fetch_media_items(conn, entry_ids: list[int]) -> dict:
|
|||
for r in rows:
|
||||
result.setdefault(r["diary_id"], []).append({
|
||||
"id": r["id"], "url": r["url"],
|
||||
"media_type": r["media_type"], "sort_order": r["sort_order"]
|
||||
"media_type": r["media_type"], "sort_order": r["sort_order"],
|
||||
"is_cover": r["is_cover"],
|
||||
})
|
||||
return result
|
||||
|
||||
|
|
@ -109,7 +110,11 @@ def _entry_dict(row, dog_ids_map: dict, media_map: dict = None) -> dict:
|
|||
e = dict(row)
|
||||
e["tags"] = json.loads(e["tags"]) if e["tags"] else []
|
||||
e["dog_ids"] = dog_ids_map.get(e["id"], [e["dog_id"]])
|
||||
e["media_items"] = (media_map or {}).get(e["id"], [])
|
||||
items = (media_map or {}).get(e["id"], [])
|
||||
e["media_items"] = items
|
||||
# cover_url: Item mit is_cover=1, Fallback auf erstes Item
|
||||
cover = next((m for m in items if m.get("is_cover")), items[0] if items else None)
|
||||
e["cover_url"] = cover["url"] if cover else None
|
||||
return e
|
||||
|
||||
|
||||
|
|
@ -437,16 +442,19 @@ async def upload_media(dog_id: int, entry_id: int,
|
|||
"SELECT COALESCE(MAX(sort_order), -1) FROM diary_media WHERE diary_id=?",
|
||||
(entry_id,)
|
||||
).fetchone()[0]
|
||||
# Erstes Item eines Eintrags wird automatisch Cover
|
||||
is_cover = 1 if max_order == -1 else 0
|
||||
conn.execute(
|
||||
"INSERT INTO diary_media (diary_id, url, media_type, sort_order) VALUES (?,?,?,?)",
|
||||
(entry_id, media_url, media_type, max_order + 1)
|
||||
"INSERT INTO diary_media (diary_id, url, media_type, sort_order, is_cover) VALUES (?,?,?,?,?)",
|
||||
(entry_id, media_url, media_type, max_order + 1, is_cover)
|
||||
)
|
||||
new_id = conn.execute(
|
||||
"SELECT id FROM diary_media WHERE diary_id=? ORDER BY id DESC LIMIT 1",
|
||||
(entry_id,)
|
||||
).fetchone()["id"]
|
||||
|
||||
return {"id": new_id, "url": media_url, "media_type": media_type, "sort_order": max_order + 1}
|
||||
return {"id": new_id, "url": media_url, "media_type": media_type,
|
||||
"sort_order": max_order + 1, "is_cover": is_cover}
|
||||
|
||||
|
||||
@router.delete("/{dog_id}/diary/{entry_id}/media/{media_id}", status_code=204)
|
||||
|
|
@ -484,3 +492,24 @@ async def delete_media_legacy(dog_id: int, entry_id: int, user=Depends(get_curre
|
|||
try: os.remove(path)
|
||||
except OSError: pass
|
||||
conn.execute("UPDATE diary SET media_url=NULL WHERE id=?", (entry_id,))
|
||||
|
||||
|
||||
@router.patch("/{dog_id}/diary/{entry_id}/media/{media_id}/cover", status_code=200)
|
||||
async def set_cover_media(dog_id: int, entry_id: int, media_id: int,
|
||||
user=Depends(get_current_user)):
|
||||
"""Setzt ein Medium als Cover-Bild (is_cover=1), alle anderen auf 0."""
|
||||
with db() as conn:
|
||||
_own_dog(dog_id, user["id"], conn)
|
||||
row = conn.execute(
|
||||
"SELECT dm.id FROM diary_media dm "
|
||||
"JOIN diary d ON d.id = dm.diary_id "
|
||||
"LEFT JOIN diary_dogs dd ON dd.diary_id = d.id "
|
||||
"WHERE dm.id=? AND dm.diary_id=? AND (d.dog_id=? OR dd.dog_id=?)",
|
||||
(media_id, entry_id, dog_id, dog_id)
|
||||
).fetchone()
|
||||
if not row:
|
||||
raise HTTPException(404, "Medium nicht gefunden.")
|
||||
# Alle Items dieses Eintrags auf is_cover=0, dann das gewählte auf 1
|
||||
conn.execute("UPDATE diary_media SET is_cover=0 WHERE diary_id=?", (entry_id,))
|
||||
conn.execute("UPDATE diary_media SET is_cover=1 WHERE id=?", (media_id,))
|
||||
return {"ok": True}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue