39d19cf2f8
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>
92 lines
2.7 KiB
Python
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,
|
|
)
|