diff options
| author | Tyler Hoang <tyler@tylerhoang.xyz> | 2026-05-26 00:41:21 -0700 |
|---|---|---|
| committer | Tyler Hoang <tyler@tylerhoang.xyz> | 2026-05-26 00:41:21 -0700 |
| commit | 82e774cc044bb47a6adfa102bf36e8400e8001d5 (patch) | |
| tree | b5f1c58d0378a2d1083f518f9c189c1f51582aa5 /index.html | |
| parent | b2827329a32d3fe627a62bd4ff4191b2a3e4407f (diff) | |
films: map real api fields and use 3-star scale
films.tylerhoang.xyz/tyler/api/recent returns stars (0-3), date_watched,
poster_url, director. Wire those through and render 3 stars instead of
5. Escape user-supplied strings.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Diffstat (limited to 'index.html')
| -rwxr-xr-x | index.html | 47 |
1 files changed, 24 insertions, 23 deletions
@@ -434,42 +434,43 @@ const filmsList = document.getElementById('films-list'); if (films.length > 0) { - filmsList.innerHTML = films.map((film, idx) => { - const stars = Array.from({ length: 5 }, (_, i) => { - const fullStars = Math.floor(film.rating || 0); - const hasHalf = (film.rating || 0) % 1 !== 0 && i + 1 === Math.ceil(film.rating || 0); - if (i + 1 <= fullStars) return '<span class="star on">★</span>'; - if (hasHalf) return '<span class="star half">★</span>'; - return '<span class="star">★</span>'; - }).join(''); + const esc = s => String(s == null ? '' : s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); + filmsList.innerHTML = films.slice(0, 4).map((film) => { + const rating = Math.max(0, Math.min(3, Number(film.stars ?? film.rating ?? 0))); + const stars = Array.from({ length: 3 }, (_, i) => + i < rating ? '<span class="star on">★</span>' : '<span class="star">★</span>' + ).join(''); - let when = 'unknown'; - if (film.watchedAt) { - const d = new Date(film.watchedAt); + let when = ''; + const raw = film.date_watched || film.watchedAt; + if (raw) { + const d = new Date(raw); if (!isNaN(d)) { - const diff = Date.now() - d; - const days = Math.floor(diff / 86400000); - if (days === 0) when = 'today'; + const days = Math.floor((Date.now() - d) / 86400000); + if (days <= 0) when = 'today'; else if (days === 1) when = '1d ago'; else if (days < 7) when = days + 'd ago'; - else when = Math.floor(days / 7) + 'w ago'; + else if (days < 30) when = Math.floor(days / 7) + 'w ago'; + else when = Math.floor(days / 30) + 'mo ago'; } else { - when = String(film.watchedAt); + when = String(raw); } } - let posterStyle = `background: linear-gradient(135deg, oklch(${50 + idx * 10}% 0.12 ${30 + idx * 60}), oklch(${30 + idx * 8}% 0.08 ${280 - idx * 40}));`; - if (film.posterUrl) { - posterStyle = `background: url(${JSON.stringify(film.posterUrl)}) center / cover;`; - } + const poster = film.poster_url || film.posterUrl; + const posterStyle = poster + ? `background: url('${esc(poster).replace(/'/g, '%27')}') center / cover;` + : `background: linear-gradient(135deg, oklch(70% 0.13 220), oklch(40% 0.10 250));`; + + const sub = film.director ? esc(film.director) : (film.note ? esc(film.note) : ''); return ` <div class="film-row"> <div class="film-poster" style="${posterStyle}"></div> <div class="film-meta"> - <div class="film-title">${film.title} <span class="film-year">${film.year || ''}</span></div> - <div class="film-rating">${stars}<span style="opacity:0.6;margin-left:8px;">${when}${film.note ? '' : ''}</span></div> - ${film.note ? `<div class="film-note">${film.note}</div>` : ''} + <div class="film-title">${esc(film.title)} <span class="film-year">${esc(film.year || '')}</span></div> + <div class="film-rating">${stars}<span style="opacity:0.6;margin-left:8px;">${when}</span></div> + ${sub ? `<div class="film-note">${sub}</div>` : ''} </div> </div> `; |
