Commit Graph
16 Commits
Author SHA1 Message Date
jozamudi be47ced521 Report client fingerprint on playback failure
showStreamError + playerFail now POST the failing browser's UA,
hls.js presence, MSE support and native-HLS capability to the
events table — a broken mobile browser becomes diagnosable from
the server side instead of by guessing.
2026-08-31 19:57:57 +00:00
jozamudi 8f6d4dcf77 Mobile audit fixes: iOS input auto-zoom + tap delay
Audited on iPhone SE (320px), iPhone 13, Pixel 5 emulations:
- FOUND: login/search inputs at 13.3px (< 16px) -> iOS Safari
  auto-zooms the page on focus. Fixed: 16px in style.css AND
  login.html (separate page with own CSS).
- FOUND: no touch-action anywhere -> double-tap-zoom tap delay on
  buttons. Fixed: touch-action: manipulation on interactive elements
  in both stylesheets.
- FOUND: .pl-spin missing from prefers-reduced-motion. Added.
- VERIFIED CLEAN: no horizontal overflow at 320/390/393px, controls
  row fits, chip+gauge in viewport, episode grid 66x31 ok,
  Tap-to-play gesture flow works on BOTH hls.js and native paths
  (simulated iOS autoplay block), toast fits, zero JS errors,
  desktop playback regression OK, 18/18 API tests.
assets v=7; login.html is no-store so updates without a bump.
2026-08-31 19:40:07 +00:00
jozamudi 9b90be3a10 Player: native-HLS fallback + visible stream errors
Root cause of 'eternal spinner': a browser without working hls.js
(no MSE, or hls.min.js blocked) threw 'HLS not supported' which the
old showStreamError hid UNDER the spinner overlay, and there was no
fallback — so the player sat on Loading… forever with no /stream
request ever leaving the browser (matched the prod log exactly).

- no-hls.js + canPlayType(mpegurl)  -> v.src = proxied m3u8, native
  <video> plays it (iOS < 17 path). Verified: readyState 4, playing,
  no hls.js at all.
- no-hls.js + no native HLS        -> VISIBLE 'Stream error: ...'
  overlay with Retry (was: hidden under the spinner)
- every play() catch routes through the visible overlay now

Desktop hls.js regression: still plays. assets v=6.
2026-08-31 19:26:53 +00:00
jozamudi 733fa673c2 Player: buffering visuals — spinner ring, buffer-ahead gauge, CDN chip
- Loading/Buffering overlays now show a rotating ring, not just text
- thin gauge under the video: how much is buffered ahead (0-30s scale),
  fills while loading, drains as it plays — you can see a stall building
- corner chip: live CDN KB/s from /api/cdn, green dot OK / amber degraded,
  refreshed on every stall — explains WHY it's buffering

Verified in-browser (CDN-independent): chip reflects live state (0 KB/s
degraded), gauge fills from video.buffered, stall overlay + ring,
playing clears. 18/18 API tests. assets v=5.
2026-08-31 15:37:06 +00:00
jozamudi 2576546b43 hls.js: drop segment load cap 30s -> 10s
Dribbling CDN segments (logged at 17.8s/59.7s) made the player stare
at 'Buffering…' for half a minute per attempt. Cap total load at 10s
so a retry kicks in sooner (timeoutRetry still 6x, backoff 1-20s);
fast segments (0.1-0.2s) are unaffected.
2026-08-31 14:10:11 +00:00
jozamudi 07554e4aa8 Playback page: show 'E{n} — episode title' line under the video
The name was only in the grid tooltip (hover-only, invisible on touch).
Add a persistent now-playing line between the player and controls:
'E1 — Ryomen Sukuna'. setNowEp() runs on every play() and refreshes
when the async Jikan name lands late. Bumped asset version to v=3.
2026-08-31 02:05:00 +00:00
jozamudi 8840a883c0 Cache-bust app.js/style.css from index.html (?v=2)
index.html is no-store (always fresh) but app.js/style.css are cached
max-age=300 in the browser — and possibly in the HTTPS proxy — so users
kept seeing the pre-fix UI (episode names in buttons) long after the
removal shipped. A versioned URL from the always-fresh index.html
bypasses both caches. Bump ?v= on every app.js/style.css edit.
2026-08-31 01:58:55 +00:00
jozamudi c2461f8fba UI/UX polish: a11y, player controls, undo, stats ranges
- toasts: aria-live region + action button (used for history delete Undo)
- history cards: keyboard-reachable title link; delete = remove + Undo
  (re-POSTs the entry; no full list re-render)
- fail(): home/retry affordances instead of a dead-end error line
- per-view document.title + move focus to the new view on render
- player controls: episode jump (E#), auto-next toggle (persisted),
  picture-in-picture (when supported)
- auto-next off: 'Next: En' toast with Play instead of the countdown
- no-results search + empty home: recent-search chips (shared helper)
- stats: 30d/90d/1y period chips (api already supported days=N)
- search input: autocapitalize/spellcheck off; reduced-motion disables
  shimmer/toast animation; username in the menu on narrow screens
2026-08-30 20:53:55 +00:00
jozamudi 1de58629a0 UX: remember sub/dub + filler toggle, hero time-left, next-ep CTA,
history filter chips, per-ep progress bars, stats in user menu

- sub/dub choice persists per anime (localStorage aniweb_lang_<slug>),
  so search/reload no longer drops dub watchers back to sub
- 'hide fillers' toggle persists (aniweb_ff)
- continue-watching hero shows '~Xm left' when position+duration known
- detail page: prominent 'next episode' button when the resumed ep is
  finished (auto-removed on play)
- ?history: all / watching / completed / on-hold filter chips (client-side)
- ep list: thin progress bar on partially-watched episodes
- stats page link moved into the user menu (always reachable)
- search results show a count line
2026-08-24 17:38:11 +00:00
jozamudi bb1297f8e2 ui: watched-episode state (dimmed + check, next-up highlight), favicon 2026-08-19 18:49:29 +00:00
jozamudi 2ec0f8a034 ui: loading states, player failure overlay + retry, toasts, back nav, full history page with delete
Bundle 1-4+7 from PLAN.md: skeleton loaders, player Loading/Buffering/fatal overlay with Retry, action toasts, back-to-home crumb + logo fix, ?history full-history view with per-entry delete (GET /api/history?all=1, DELETE /api/history). 14/14 tests green.
2026-08-19 18:20:42 +00:00
jozamudi e0f5427002 ui: move logout into the hamburger menu too 2026-08-19 16:42:34 +00:00
jozamudi 1fb6db475c ui: hide delete-account behind a header hamburger menu 2026-08-19 16:41:11 +00:00
jozamudi c697500c7d multi-user auth (CLI-created accounts), per-user history, test suite
- server/users.py replaces credentials.py: users.json (0600), PBKDF2,
  one-time migration of the legacy single password to user admin
  (history.json -> history/admin.json)
- accounts are CLI-managed only: python -m server.users add|rm|list
  (no self-signup on an internet-exposed app); /signup route removed,
  login page back to username + password
- per-user 30-day signed session cookie (payload carries the
  username), per-user lockout, GET /api/me, POST /account/delete
- per-user watch history: server/data/history/<user>.json; ANIWEB_DATA
  env override for the data dir
- header chip: logged-in user, logout, delete account
- tests/: throwaway-server suite (port 8399, temp data dir) — 9 HTTP
  tests + 2 Playwright browser tests (login UI, real video playback
  behind auth); runs via python tests/run.py
2026-08-19 16:17:56 +00:00
jozamudi 6c752ae5bc fix playback: vendor hls.js locally, surface stream/API errors visibly
- jsdelivr CDN was a silent single point of failure (Hls undefined ->
  unhandled rejection -> black screen with no feedback)
- play() now catches API errors; hls fatal + video media errors render
  'Stream error: ...' under the player
- server now binds 0.0.0.0 (LAN access); was 127.0.0.1 only
2026-08-19 15:13:51 +00:00
jozamudi e4dde86d85 ani-web: FastAPI + HLS proxy over anidb.app, vanilla UI
- upstream.py: Cloudflare-proof fetchers (curl_cffi), self-checking
- app.py: /api/* routes + /stream/{token} HLS proxy (CDN has no CORS)
- static/: search -> detail/seasons/episodes -> hls.js player, history
- verified end-to-end: search chainsaw man -> ep1 dub 1080p segment
2026-08-19 15:06:43 +00:00