summaryrefslogtreecommitdiff
path: root/player-server/cmd
AgeCommit message (Collapse)Author
2026-05-20Extract app wiring from main.go into internal/app package (9a)Paul Buetow
Move all dependency wiring, background worker startup, server lifecycle (Wire, StartBackgroundWorkers, RunServer, RunWithSignal, BuildLogger) into internal/app so cmd/player/main.go becomes thin: parse flags, load config, delegate to app.RunWithSignal. Updated main_test.go to call app.Wire and app.StartBackgroundWorkers directly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20Validate AbsPath in streamer and clean up partial podcast downloads (l9+k9)Paul Buetow
l9: Add mediaRoot field to mediaStreamer. NewMediaStreamer now takes a mediaRoot string parameter; when non-empty, Open() rejects any path that does not reside under that directory (filepath.Clean prefix check), returning ErrForbidden to prevent filepath-traversal via a compromised AbsPath in the DB. Production wiring passes cfg.MediaRoot; tests that don't exercise path traversal pass "". Added TestMediaStreamerOpenRejectsPathOutsideRoot to cover the rejection path. k9: Add a defer-based cleanup guard in DownloadEpisode. After the enclosure file is written, a succeeded flag gates a deferred closure that calls dbCleanup() (undoes DB row + removes file) when persistDownloadedEpisode succeeded, or removeAndLog(path) when it did not. This ensures that any failure after the file is written — including UpdateEpisodeMedia — leaves no orphaned files on disk. The guard is disarmed by setting succeeded=true on the happy path. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20Dispatch handleError via HTTPStatuser interface (4a)Paul Buetow
handleError previously hard-coded a switch mapping every service sentinel to its HTTP status. Adding a new sentinel required editing handleError (OCP violation). Move the status onto the sentinel itself: introduce api.HTTPStatuser { HTTPStatus() int }, convert the 10 dispatched service sentinels to a typed *apiError that implements it, and turn handleError into a thin errors.As-based dispatcher. ErrShareExpired stays a plain sentinel because it is handled inline by share handlers and never reaches handleError. Sentinel messages are aligned with what handleError used to emit (e.g. ErrForbidden is now "forbidden" instead of "access denied", ErrAlreadyBootstrapped is "bootstrap already complete") so the dispatcher can use err.Error() uniformly. Wrapped errors (fmt.Errorf("%w: ...", ErrNotFound, ...)) keep their context in the response body, which is a minor improvement over the previous behaviour that collapsed everything to the bare sentinel message. errors.Is keeps working via pointer equality on the *apiError sentinels.
2026-05-20Inject clock.Clock into api.Server and replace time.Now() in share/auth handlersPaul Buetow
handlers_share.go (handleCreateShare share-expiry) and handlers_auth.go (setSessionCookie and apiTokenExpiresAt) previously called time.Now() directly, which made time-dependent semantics impossible to assert deterministically in tests. They now use s.clk.Now(), where s.clk is a clock.Clock injected through ServerDeps.Clock (nil-default to clock.RealClock{} so existing callers keep working unchanged). apiTokenExpiresAt is promoted to a method on *Server so it can reach the injected clock. Production wiring in cmd/player/main.go passes deps.clk so handlers share the same time source as the rest of the services. Two new unit tests (handlers_share_test.go) use clock.MockClock to assert handleCreateShare and setSessionCookie compute their expiry timestamps from the injected clock rather than the wall clock. While propagating, this commit also includes a mechanical fix-up for the pathID(...) signature change (now returns int64 + error) across the API handler files so the package still builds; the additional err-check makes malformed path variables produce 400 instead of silently parsing as zero. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19Introduce thumb.Resolver to decouple browseService from filesystemPaul Buetow
browseService.GetThumbnail previously called os.Stat directly, coupling the service layer to the filesystem and forcing tests to write temporary files. Introduce a thumb.Resolver interface plus an FSResolver default, inject it into browseService, and wire NewMediaServiceWithDeps so production constructs the resolver explicitly. Tests can now swap in a fake Resolver without touching disk. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19Complete s9 + aa: NewServerWithLogger returns error; share-page renderer ↵Paul Buetow
extracted Two related refactors that converged on internal/api/server.go. s9 — NewServerWithLogger no longer panics on nil deps.Config. Returns (*Server, error) so cmd/player/main.go can log a clear message and exit cleanly when wiring is misconfigured. All callers updated, including api unit tests that construct a Server directly. aa — Share-page HTML rendering moved out of handlers_share.go into a new internal/web package. internal/web/sharepage.go owns SharePageRenderer and the private injectShareMedia helper; the api handler now calls s.shareRenderer.Render(...) and routes errors via http.Error. injectShareMedia coverage was moved alongside it in internal/web/sharepage_test.go; the duplicate tests in handlers_more_test.go were removed (placeholder comment left so the reference is greppable). Server gained one new field (shareRenderer *web.SharePageRenderer) and one new constructor line; production wiring uses deps.StaticFS. Background: both tasks were initially worked by separate sub-agents that stopped mid-edit when usage limits hit; their WIP was preserved in a stash and finalized in this commit. Tests rerun from a clean state: full Go unit suite green, 25/25 LLM e2e, 22/22 Playwright. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19Unify panic recovery into service.RecoverWorkerPaul Buetow
cmd/player/main.go had recoverBackgroundWorkerPanic and internal/service/recover.go had handleWorkerPanic implementing the same log-and-stack panic recovery. Export the service variant as RecoverWorker (unchanged signature), use it from main.go, and drop the duplicate helper plus the now-unused runtime/debug import. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18Add playback hints endpoint for client codec/container decisionsPaul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17Add Bearer token authentication alongside session cookiesPaul Buetow
2026-05-17Restructure repo: move Go server into player-server/Paul Buetow