Files
xTeVe/tasks/todo.md
T
nathan 3a3ba861dc
continuous-integration/drone/push Build encountered an error
Phase 5a: persistent websocket with request ids and reconnect
Client (ts/network_ts.ts): one WebSocket per page. Commands queue and go
out one at a time with a client-chosen id; the response is matched on the
echoed id (or to the in-flight request for older servers). 12 s timeout
per request, exponential-backoff reconnect (0.5 s to 10 s), the in-flight
request is retried after a reconnect, and log polls are de-duplicated so
they cannot pile up behind a stalled connection. The old global flag that
silently dropped any request made while another was in flight is gone.

Server (src/webserver.go): the /data/ handler now serves any number of
commands on one connection (it used to break out of its loop after the
first reply without closing the socket, leaving it open and deaf; the old
client papered over that by opening a new socket per request). Connection
closed on exit, request id echoed in the response.

Test: TestWSServesMultipleCommandsPerConnection.
2026-09-26 13:19:38 +10:00

8.1 KiB

xTeVe improvement checklist

Detailed rationale, file references and effort estimates: tasks/improvement-plan.md. Status: Phases 0, 1, 2, 3 done on branch improvements (2026-09-26); Phase 5 in progress.

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
  • Section-level re-render instead of full createLayout()
  • Virtualised mapping table
  • Split menu_ts.ts; addEventListener; data-driven settings rows
  • Accessibility follow-ups; light theme via prefers-color-scheme
  • Consolidate CSS layers

Phase 6: Optional features

  • Regex filters
  • Per-playlist tuner limit and buffer choice
  • Bulk channel edit
  • Versioned backup/restore
  • Dummy EPG durations

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.