Files
intotheeast-com/services/travel-memories/app/routes/albums.py
T
m038 39d19cf2f8 feat: Phase 1 album selection with resume/start-over
Implements GET / listing Immich albums with resume badge, POST /select
creating TripState and redirecting to /triage; graceful error display
when Immich is unreachable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 16:18:38 +02:00

92 lines
2.7 KiB
Python

from pathlib import Path
from flask import Blueprint, current_app, redirect, render_template, request
from app.immich import ImmichClient
from app.state import TripState, Photo, load_state, save_state
bp = Blueprint("albums", __name__)
def _client():
return ImmichClient(current_app.config["IMMICH_URL"],
current_app.config["IMMICH_API_KEY"])
@bp.get("/")
def index():
try:
albums = _client().list_albums()
error = None
except ConnectionError as e:
albums = []
error = str(e)
state_dir = Path(current_app.config["STATE_DIR"])
for album in albums:
album["has_state"] = (state_dir / f"{album['id']}.json").exists()
return render_template("phase1.html", albums=albums, error=error,
current_phase="", album_id=None,
phase_stale=[], notes_content="")
@bp.post("/select")
def select():
album_ids = request.form.getlist("album_ids[]")
grav_trip_slug = request.form["grav_trip_slug"].strip()
start_over = request.form.get("start_over") == "1"
if len(album_ids) == 1:
primary_id = album_ids[0]
else:
primary_id = "__merged__" + "_".join(sorted(album_ids))
existing = load_state(primary_id, current_app)
if existing and not start_over:
return redirect(f"/{existing.phase}?album_id={primary_id}")
# Fetch and merge assets, deduplicating by asset ID
all_assets = {}
album_name_parts = []
for aid in album_ids:
album = _client().get_album(aid)
album_name_parts.append(album["albumName"])
for asset in album["assets"]:
if asset["id"] not in all_assets:
all_assets[asset["id"]] = asset
photos = [
Photo(id=a["id"], original_filename=a["originalFileName"],
local_datetime=a["localDateTime"])
for a in sorted(all_assets.values(), key=lambda x: x["localDateTime"])
]
for i, p in enumerate(photos):
p.order = i
state = TripState(
album_id=primary_id,
album_name=", ".join(album_name_parts),
grav_trip_slug=grav_trip_slug,
photos=photos,
)
save_state(state, current_app)
return redirect(f"/triage?album_id={primary_id}")
# TODO(task-6): replace this stub with the real triage route
@bp.get("/triage")
def triage():
album_id = request.args.get("album_id", "")
notes_content = ""
phase_stale = []
if album_id:
state = load_state(album_id, current_app)
if state:
notes_content = state.notes
phase_stale = state.phase_stale
return render_template(
"base.html",
current_phase="triage",
album_id=album_id,
notes_content=notes_content,
phase_stale=phase_stale,
)