Files
xTeVe/tasks/todo.md
T
nathan 320eaa1b28
continuous-integration/drone/push Build encountered an error
Phase 2a: buffer state under one lock, atomic tuner limit, context-driven external buffer
- src/buffer_state.go replaces the two sync.Maps plus a global RWMutex with
  one bufferMu guarding a map of *Playlist. Each stream has one shared
  bufferStream (URL, folder, status, client count, error, cancel hook);
  downloaders keep a private ThisStream and publish through helpers.
  Playlist.Clients / ThisClient / ClientConnection are gone: there was one
  client counter per stream in two places that could disagree.
- bufferAcquireStream does the tuner check and the registration under the
  same lock, so two clients tuning at once cannot both pass the limit.
  bufferReleaseClient removes the stream when the last client leaves,
  cancels its process and deletes its segment folder.
- bufferingStream rewritten: waits on r.Context() instead of the deprecated
  CloseNotifier, sets Content-Type before WriteHeader (the old code set
  headers after and one was literally named "Content-Length:"), flushes
  each segment to the client, no defer inside the segment loop.
- connectToStreamingServer: shared streamHTTPClient with dial, TLS and
  response-header timeouts (no overall timeout, bodies are endless); the
  deferred Body/segment closes inside the redirect and read loops are now
  explicit closes, so a multi-hour stream no longer accumulates them.
- thirdPartyBuffer rewritten around exec.CommandContext: the process is
  killed when the last client leaves or when no usable data arrives within
  20 s (time.AfterFunc watchdog, no leaked goroutine); cmd.Start error is
  checked; no panic; one file handle per segment. ffmpeg and VLC command
  lines come from buildFFmpegArgs / buildVLCArgs, which are unit tested.
- Data.Cache.StreamingURLS is guarded by streamingURLsMu and persisted
  from a snapshot.
- Tests (src/buffer_test.go, run with -race): restream shares one provider
  connection, six concurrent tunes against a tuner limit of two, 50-way
  acquire/release contention, cleanup and cancel on last release.
2026-09-26 13:14:48 +10:00

7.1 KiB

xTeVe improvement checklist

Detailed rationale, file references and effort estimates: tasks/improvement-plan.md. Status: Phase 0 committed on branch improvements 2026-09-26.

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
  • Real mutex for screen log
  • http.Server with timeouts; timeouts on all outbound clients
  • imgcache: download outside the lock
  • Atomic write helper (temp + fsync + rename)
  • Buffer: headers set before WriteHeader, bogus Content-Length: header gone, segments flushed to the client as they arrive. Others in 2b
  • Error hygiene: no-op closures, os.Exit/panic outside main, ignored results

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 with request IDs, queue, backoff reconnect
  • 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.