Files
xTeVe/tasks/todo.md
T
nathan 014a3b04d2
continuous-integration/drone/push Build is passing
Phase 5b and Phase 6: TypeScript restructure, jsdom tests, regex filters and per-playlist buffer in the UI
TypeScript:
- menu_ts.ts split into menu.ts, content.ts, popup.ts and xmltv.ts; the
  other files drop their _ts suffix. Bundle order fixed in ts/tsconfig.json.
- All 94 string event handlers (setAttribute("onclick", "javascript: ..."))
  replaced with addEventListener closures. changeButtonAction, which
  rewrote an onclick attribute from select values, is gone.
- Settings rows are generated from one SETTINGS_FIELDS table instead of
  ~20 copy-pasted blocks; rendered HTML is byte-identical to before.
  saveSettings now serialises password inputs, so a changed Plex token is
  actually sent (the server treats the mask as unchanged).
- createLayout rebuilds the menu list only when the set of visible items
  changes, so focus in the menu survives a refresh.
- announce()/alertUser(): alerts are mirrored into an aria-live region.
- Filter popup offers a third type, Regular Expression (regex-filter), and
  the filter table labels it. Playlist popups get a Buffer select
  (default / none / xTeVe / FFmpeg / VLC) saved as the playlist's
  "buffer" parameter; the tuner field stays editable when the
  playlist's own buffer is active.

Tests: tests/ holds 27 jsdom tests (Node's built-in runner, jsdom pinned)
that load the built bundle with a fixture server payload: menu visibility
rules, mapping table renders names as text, settings panel fields, popup
flows, sorting, bulk select, layout refresh keeps nodes, live region,
regex option, buffer select. npm test runs in the Drone webui-check step.
README-DEV documents the layout and the test workflow.
2026-09-26 13:39:35 +10:00

9.4 KiB

xTeVe improvement checklist

Detailed rationale, file references and effort estimates: tasks/improvement-plan.md. Status: all phases done on branch improvements (2026-09-26). Open: the agent.md design-system reference (user decision) and two items kept by design (Host-derived URLs, no user roles).

Phase 0: Hygiene

  • .gitignore (.gocache/), deleted .gocache/, extended .dockerignore
  • agent.md / skill.md git-ignored and docker-ignored (left in place)
  • Version drift fixed (xteve.go now 0201) and Drone drift check added
  • Auto-updater deleted (BinaryUpdate, internal/up2date, GitHub/Update structs, xteveAutoUpdate + update.url settings, UI rows, en.json); migrations kept in migrate.go; kardianos/osext gone
  • go 1.27.1; Dockerfile and Drone golang images pinned to 1.27.1
  • Four go vet unreachable-code warnings fixed (vet clean)
  • staticcheck baseline via go run honnef.co/go/tools/cmd/staticcheck@latest ./...: 408 findings (S1002 113, SA5008 79, S1039 67, S1038 40, S1023 35, ST1005 20, SA1019 14, SA4006 9, misc 11). Mostly style; SA5008/SA4006/SA1019 worth a pass in Phase 3 cleanup

Phase 1: Security

  • textContent for provider-controlled strings (cells, client info, in-place edits, logs, popup descriptions with an explicit static-HTML flag)
  • Websocket uses gorilla's same-origin check; token read from the HttpOnly cookie (query param kept for legacy clients); payload/token console logging removed. Tests in src/websocket_test.go
  • Cookie HttpOnly, SameSite=Strict, Path=/, cleared on logout
  • Wizard GET no longer mutates AuthenticationWEB; the wizard page just bypasses login while active
  • uploadLogo filename sanitised (base name, image extensions only)
  • Zip-slip guard in extractZIP (also no more defer-in-loop there)
  • ffmpeg.path/vlc.path must be a regular file named ffmpeg/vlc/cvlc; stream URLs handed to the external buffer must use a network scheme
  • /download/ requires the web session when web auth is on
  • [~] Host-header-derived URLs kept by design (LAN, many interfaces; URLs must match how the client reached the server)
  • bcrypt for new passwords and credential changes; legacy SHA256 records verified in constant time and upgraded on first login; username compare constant-time
  • Expired tokens evicted on every new session; URL/Basic auth no longer mint tokens at all (AuthenticateUser)
  • [~] Not applicable: xTeVe has no roles, every web user is an administrator by design (documented in README security notes)
  • settings.json written 0600; Plex token masked in every payload to the UI, mask round-trips as "unchanged" on save
  • Decide default for web auth on fresh installs (keep off; LAN only, decided 2026-09-25)

Phase 2: Streaming stability

  • Race tests in src/buffer_test.go: restream shares one provider connection, tuner limit atomic under 6 concurrent tunes, acquire/release contention, cleanup on last client; run with -race
  • New src/buffer_state.go: one bufferMu, playlists by pointer, one bufferStream per stream (status, client count, error, cancel hook); downloaders keep a private working copy and publish through helpers. BufferInformation/BufferClients/Lock removed
  • Tuner check and registration under one lock (bufferAcquireStream); last client out removes the stream, cancels its process and deletes its folder
  • streamingURLsMu around Data.Cache.StreamingURLS; persisted from a snapshot
  • No more defer inside the buffer read/redirect loops (compression done in Phase 1; imgcache in 2b)
  • thirdPartyBuffer rewritten: exec.CommandContext cancelled when the last client leaves or the 20 s startup watchdog fires; buildFFmpegArgs/buildVLCArgs tested; no panic, cmd.Start checked, one file handle per segment. Client loop uses r.Context() instead of CloseNotifier
  • logMu guards WebScreenLog and notifications; accessors logAppend/webScreenLogSnapshot/notificationsSnapshot; ring buffer keeps the newest N (was dropping them)
  • http.Server with ReadHeaderTimeout 15 s and IdleTimeout 120 s (no write timeout, streams are long-lived); providerHTTPClient 5 min, apiHTTPClient 30 s, imgcache client 30 s, stream client with dial/TLS/header timeouts
  • imgcache downloads outside the lock; cache URL bug fixed (was a filesystem path); query-string URLs no longer produce unservable file names
  • writeFileAtomic (temp + fsync + rename) behind writeByteToFile, saveMapToJSONFile, writePrivateFile and the auth database
  • Buffer: headers set before WriteHeader, bogus Content-Length: header gone, segments flushed as they arrive. xepg file-removal bug, dead range mutation, API double write, WS struct reuse, unchecked assertions in data/backup/provider/screen, notification eviction by age: all fixed with tests
  • Error hygiene: no panic/os.Exit/log.Fatal outside main; SIGINT/SIGTERM handled in main via src.Shutdown(); fatal start-up errors exit 1

Phase 3: Build, embed, CI, Docker

  • //go:embed via html/embed.go; deleted webUI.go, html-build.go, cmd/webui-gen; ETag + Cache-Control on static assets
  • i18n dropped: 254 placeholders inlined, en.json deleted, only HTML pages templated (authenticationErr)
  • package.json + ts/tsconfig.json (tsc 5.9.3, ES2020, single outFile html/js/app.js, committed, CI-verified); 10 dead JS files deleted; 6 tsc errors fixed incl. a real ASI bug in the search shortcut
  • CI: vet, gofmt check, staticcheck v0.8.1 (config in staticcheck.conf), bundle freshness check. go test -race still to add once tests exist
  • Version-tagged images (amd64 only)
  • PUID/PGID via su-exec at runtime; static-ffmpeg pinned to 7.1.1; VOLUME /xteve/config; /xteve removed from LEGACY_CONFIG_DIRS; compose files pull from the registry and document SSDP/host networking
  • /healthz endpoint; Dockerfile healthcheck uses it
  • README-DEV.md and fork-specific README.md (about, container usage, env vars, security notes)
  • staticcheck 399 -> 0; dead code and duplicates removed; ioutil/rand.Seed gone. Real bugs fixed on the way: leaked file handle per ffmpeg segment, unchecked http.NewRequest, migration writing null xepg.json, silent user-write failures, unwritable config/temp dir now fatal at start
  • Resolve missing docs/design-system/ referenced by agent.md (user decision: agent.md is a personal, git-ignored file)

Phase 4: Data model and performance

Not planned (lineup is ~170 channels, decided 2026-09-25). See plan for the reference list.

Phase 5: Frontend architecture

  • Persistent websocket: one socket per page, queued requests with ids echoed by the server, 12 s per-request timeout, exponential-backoff reconnect, log-poll dedupe. Server keeps serving on one connection and closes it on exit (upstream leaked one open socket per request). Test: TestWSServesMultipleCommandsPerConnection
  • createLayout() rebuilds the menu list only when the set of visible items changes; client info and status cards refresh in place (jsdom test keeps the same nodes and focus)
  • [~] Virtualised mapping table skipped: ~170 channels render instantly, and sorting/search operate on DOM rows, so virtualisation would add risk for no gain (same reasoning as dropping Phase 4)
  • menu_ts.ts split into menu.ts, content.ts, popup.ts, xmltv.ts (all _ts suffixes dropped); all 94 string on* handlers replaced by addEventListener; settings rows generated from SETTINGS_FIELDS; password fields now saved (Plex token); 27 jsdom tests under tests/ run by npm test in CI
  • Live-region announcements alongside alerts, lang, single h1, <main> landmark; light theme via prefers-color-scheme with data-theme override (contrast measured). <input type=button> kept because every button style keys on it
  • CSS split into tokens / base / components / layout, every colour a token, screen.css removed

Phase 6: Optional features

  • Regex filters: regex-filter type (validated on save, cached compile, matches name, then group, then raw line) with a popup form and table label; tested in Go and jsdom
  • Per-playlist tuner limit already existed. Per-playlist buffer: buffer select in the playlist popup (default / none / xTeVe / FFmpeg / VLC) backed by bufferModeFor; tuner field stays editable when the playlist's own buffer is active
  • Already present in the fork (bulk edit with shift-range selection); nothing to add
  • Already present: restore checks the backup's version against System.Compatibility; writes are now atomic (Phase 2)
  • Already present: xTeVe Dummy offers several durations in the mapping dropdown

Review

Phase 0 (2026-09-26, branch improvements)

  • go build, go vet, go test ./... all clean. src/webUI.go regenerated.
  • Smoke run against an empty config dir on port 34499: /web/, /lineup_status.json, /discover.json all 200, no errors logged.
  • Not verified locally: Docker image build (no daemon on this machine); Drone will cover it.
  • Compiled JS in html/js/ was hand-edited to mirror the TS change because a current tsc does not reproduce the committed output (different shim, 5 type errors in menu_ts.ts). Phase 3 replaces this with a pinned toolchain.
  • html/js/menu.js still mentions xteveAutoUpdate; it is one of the ten dead legacy files scheduled for deletion in Phase 3.
  • Settings.Branch (git.branch) was kept: it still drives whether the build number is shown in the UI and XMLTV header.