summaryrefslogtreecommitdiff
path: root/routers/films.py
diff options
context:
space:
mode:
authorTyler Hoang <tyler@tylerhoang.xyz>2026-05-06 17:12:46 -0700
committerTyler Hoang <tyler@tylerhoang.xyz>2026-05-06 17:12:46 -0700
commit4bbafdd460945eb506ddb07b9068731245708812 (patch)
tree3a3ac9151fb9db3d57af10d781658c13b1a2cfdd /routers/films.py
parent21f0cb70ca8f6c1731bf0e514e2668f42ea00718 (diff)
Add search, filter, and sort functionality to film shelves
- Add _SORT_COLUMNS dict to routers/films.py with 8 sort options - Extend _get_shelf_query to accept q (search) and sort parameters - Update /films/partial endpoint to accept q/sort query params and pass search_active to template to suppress month grouping when searching - Add filter bar (search input + sort select) to templates/index.html - Add data-shelf attribute to #film-feed for JS to read current shelf - Rewrite infinite scroll JS to support debounced search (300ms), feed reset on filter/sort change, and pass params on all fetches Filters text search by title OR director (case-insensitive ilike). Sort options: date watched (newest/oldest), title (A-Z/Z-A), year, stars. Month grouping disabled when search is active. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Diffstat (limited to 'routers/films.py')
-rw-r--r--routers/films.py30
1 files changed, 27 insertions, 3 deletions
diff --git a/routers/films.py b/routers/films.py
index afcf0ae..ec56c0a 100644
--- a/routers/films.py
+++ b/routers/films.py
@@ -176,9 +176,29 @@ def _notice_context(
}
-def _get_shelf_query(db: Session, shelf: str):
+_SORT_COLUMNS = {
+ "date_watched_desc": [Film.date_watched.desc().nullslast(), Film.id.desc()],
+ "date_watched_asc": [Film.date_watched.asc().nullslast(), Film.id.asc()],
+ "title_asc": [Film.title.asc(), Film.id.asc()],
+ "title_desc": [Film.title.desc(), Film.id.desc()],
+ "year_desc": [Film.year.desc().nullslast(), Film.id.desc()],
+ "year_asc": [Film.year.asc().nullslast(), Film.id.asc()],
+ "stars_desc": [Film.stars.desc(), Film.id.desc()],
+ "stars_asc": [Film.stars.asc(), Film.id.asc()],
+}
+
+
+def _get_shelf_query(db: Session, shelf: str, q: str | None = None, sort: str | None = None):
"""Get a query for films on a shelf, ordered appropriately."""
query = db.query(Film).filter(Film.shelf == shelf)
+
+ if q:
+ term = f"%{q}%"
+ query = query.filter(Film.title.ilike(term) | Film.director.ilike(term))
+
+ if sort and sort in _SORT_COLUMNS:
+ return query.order_by(*_SORT_COLUMNS[sort])
+
if shelf == "diary":
return query.order_by(Film.date_watched.desc(), Film.created_at.desc(), Film.id.desc())
elif shelf == "queue":
@@ -379,18 +399,21 @@ def get_films_partial(
shelf: str = Query("diary"),
offset: int = Query(0),
limit: int = Query(20),
+ q: str | None = Query(None),
+ sort: str | None = Query(None),
db: Session = Depends(get_db),
):
"""Returns partial HTML for pagination. Used by infinite scroll."""
if shelf not in ALLOWED_SHELVES:
raise HTTPException(status_code=400, detail="Invalid shelf.")
- query = _get_shelf_query(db, shelf)
+ search_active = bool(q and q.strip())
+ query = _get_shelf_query(db, shelf, q=q or None, sort=sort or None)
total = query.count()
films = query.offset(offset).limit(limit).all()
has_more = (offset + limit) < total
- if shelf == "diary":
+ if shelf == "diary" and not search_active:
grouped_films = _group_films_by_month(films)
else:
grouped_films = None
@@ -401,6 +424,7 @@ def get_films_partial(
films=films,
grouped_films=grouped_films,
active_shelf=shelf,
+ search_active=search_active,
)
response = HTMLResponse(html)