Build REST API for channel/collection/schedule CRUD #2
Closed
opened 2026-02-26 23:45:57 +01:00 by timothy
·
3 comments
No Branch/Tag Specified
main
renovate/meziantou.analyzer-3.x
renovate/nuget-sixlabors.imagesharp-vulnerability
913-scripted-playout-401
929-vuln-pkcs-imagesharp-v4
renovate/dotnet-monorepo
release/v26.15.0-notes
renovate/lucene.net
renovate/cliwrap-3.x
issue-806-guard-populations
scratch/767b-poisoned
scratch/767b-control
release/v26.14.0-notes
release/v26.14.0
renovate/sqlitepclraw.bundle_e_sqlite3-3.x
docs/510-skill-logo-bug-policy
fix/510-watermark-resolution-policy
fix/629-verdict-classifier-falseopens
fix/609-decisions-edit-token-scope
issue-135-clear-to-none
release/v26.12.0-notes
fix/409b-lastscan-api-parity
fix/401-updatechannel-mirror-422
fix/327-playlist-rename-validation
fix/410-scancancel-log-level
fix/409-447-librariesscreen-neverscanned
fix/338-zap-exit-code
fix/367-plex-budget-message
fix/310-debom-legacy-cs
ci/604-lane-rebalance
feat/388-design-mirror
feat/247-test-ownership
feat/247-primary-action
feat/357-player-owned-playback
feat/357-jellyfin-plugin-poc
fix/289-mcp-hardening
issue58-mcp
feat/244-channels-extract
ci/auto-bump-prod-compose
feat/multi-rerun-collections-api
feat/collections-api
feat/quick-wins
feat/185-docs-part2
feat/140-collections-screen
feat/146-channel-edit
feat/147-classic-ui-link
issue22-renovate-dashboard
feat/91-cutover
feat/63-composite-create
feat/65-library-browse
feat/85-epg
feat/86-schedule-editor
feat/109-dashboard-data
feat/99-session-tracking
fix/dockerfile-node-tag
feat/59-spa-foundation
docs/59-ui-redesign-brief
feat/102-json-guide
feat/111-schedule-durations
feat/104-artwork-upload
feat/103-media-sources-api
feat/playouts-read-api
feat/108-health-api
feat/105-picker-list-endpoints
issue-97-channel-state-api
issue42-jellyfin-musicvideos
issue46-rest-api-error-contract
dependabot/nuget/ErsatzTV.FFmpeg.Tests/multi-d307a2e06f
qsv-improvements
hdr-vulkan-cuda-test
v26.16.0
v26.15.0
v26.14.0
v26.13.0
v26.12.0
v26.11.0
v26.10.0
v26.9.0
v26.8.0
v26.7.0
blazor-final
v26.6.0
v26.5.0
v26.4.0
v26.3.1
v26.3.0
v26.2.0
v26.1.1
v26.1.0
v25.9.0
v25.8.0
v25.7.1
v25.7.0
v25.6.0
v25.5.0
v25.4.0
v25.3.1
v25.3.0
v25.2.0
v25.1.0
v0.8.8-beta
v0.8.7-beta
v0.8.6-beta
v0.8.5-beta
v0.8.4-beta
v0.8.3-beta
v0.8.2-beta
v0.8.1-beta
v0.8.0-beta
v0.7.9-beta
v0.7.8-beta
v0.7.7-beta
v0.7.6-beta
v0.7.5-beta
v0.7.4-beta
v0.7.3-beta
v0.7.2-beta
v0.7.1-beta
v0.7.0-beta
v0.6.9-beta
v0.6.8-beta
v0.6.7-beta
v0.6.6-beta
v0.6.5-beta
v0.6.4-beta
v0.6.3-beta
v0.6.2-beta
v0.6.1-beta
v0.6.0-beta
v0.5.8-beta
v0.5.7-beta
v0.5.6-beta
v0.5.5-beta
v0.5.4-beta
v0.5.3-beta
v0.5.2-beta
v0.5.1-beta
v0.5.0-beta
v0.4.5-alpha
v0.4.4-alpha
v0.4.3-alpha
v0.4.2-alpha
v0.4.1-alpha
v0.4.0-alpha
v0.3.8-alpha
v0.3.7-alpha
develop
v0.3.6-alpha
v0.3.5-alpha
v0.3.4-alpha
v0.3.3-alpha
v0.3.2-alpha
v0.3.1-alpha
v0.3.0-alpha
v0.2.5-alpha
v0.2.4-alpha
v0.2.3-alpha
v0.2.2-alpha
v0.2.1-alpha
v0.2.0-alpha
v0.1.5-alpha
v0.1.4-alpha
v0.1.3-alpha
v0.1.2-alpha
v0.1.1-alpha
v0.1.0-alpha
v0.0.62-alpha
v0.0.61-alpha
v0.0.60-alpha
v0.0.59-alpha
v0.0.58-alpha
v0.0.57-alpha
v0.0.56-alpha
v0.0.55-alpha
v0.0.54-alpha
v0.0.53-alpha
v0.0.52-alpha
v0.0.51-alpha
v0.0.50-alpha
v0.0.49-prealpha
v0.0.48-prealpha
v0.0.47-prealpha
v0.0.46-prealpha
v0.0.45-prealpha
v0.0.44-prealpha
v0.0.43-prealpha
v0.0.42-prealpha
v0.0.41-prealpha
v0.0.40-prealpha
v0.0.39-prealpha
v0.0.38-prealpha
v0.0.37-prealpha
v0.0.36-prealpha
v0.0.35-prealpha
v0.0.34-prealpha
v0.0.33-prealpha
v0.0.32-prealpha
v0.0.31-prealpha
v0.0.30-prealpha
v0.0.29-prealpha
v0.0.28-prealpha
v0.0.27-prealpha
v0.0.26-prealpha
v0.0.25-prealpha
v0.0.24-prealpha
v0.0.23-prealpha
v0.0.22-prealpha
v0.0.21-prealpha
v0.0.20-prealpha
v0.0.19-prealpha
v0.0.18-prealpha
v0.0.17-prealpha
v0.0.16-prealpha
v0.0.15-prealpha
v0.0.14-prealpha
v0.0.13-prealpha
v0.0.12-prealpha
v0.0.11-prealpha
v0.0.10-prealpha
v0.0.9-prealpha
v0.0.8-prealpha
v0.0.7-prealpha
v0.0.6-prealpha
v0.0.5-prealpha
v0.0.4-prealpha
v0.0.3-prealpha
v0.0.2-prealpha
v0.0.1-prealpha
Labels
Clear labels
ad-hoc
api
bug
ci-cd
content
dependencies
enhancement
frontend
in-progress
jellyfin
parked
priority: high
priority: low
priority: medium
review
security
One-off / ad-hoc work not tracked by a dedicated issue
REST API / HTTP endpoints
Something isn't working
Build, test, deploy pipeline
Channel content / schedules / playlists
Dependency updates (Renovate)
New feature or improvement
ChicoryTV React SPA frontend
Claimed by an active session — do not pick up
Jellyfin tuner / IPTV integration
Excluded from automatic queue pickup; work only when explicitly selected
Adversarial review finding
Security / vulnerability fix
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: timothy/ersatztv#2
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
ErsatzTV has no REST API for creating, updating, or deleting channels, collections, schedules, or playouts. The only way to manage these is through the Blazor Server UI or direct SQLite database manipulation.
Direct DB manipulation is fragile because:
ProgramScheduleItem→ProgramScheduleOneItem) — must insert into subtypesProposed API
REST endpoints under
/api/following the existing pattern:Channels
Collections
Schedules
Playouts
Implementation Approach
ErsatzTV.Application/ErsatzTV/Controllers/Phase 1 complete → Phase 2 underway
Phase 1 investigation (4 parallel code-grounded agents) + design is signed off. Design doc landed at
docs/rest-api.md.Key findings:
ProgramScheduleItemTPT subtype tables already exist.Either<BaseError, T>→ the API is mostly thin controllers over existing handlers.Decisions:
Api__WriteKey), decoupled from the IPTV JWT toggle → no change needed on Jellyfin/Dispatcharr (they only consume/iptv/*reads, untouched). Reusing JWT was rejected because its single global toggle also gates the IPTV feeds and its tokens expire in 1 day.Location/ 200 / 204 / 404 / 422 (+NotFoundErrortype). Request DTOs in,*ViewModels out.Increment plan (this issue = tracker):
Each slice = one branch = one PR (
test+migrationsgreen to merge). Work is isolated in a git worktree to avoid colliding with the parallel #28 session onmain.Progress update
#35 / REST API #2b is complete and merged via PR #45. Main CI run 173 passed build/test, migration integrity, and image build/push.
Follow-ups created from completion review:
ApiResultscurrently returns plain error values for 404/422 while OpenAPI documentsProblemDetails. This is cross-cutting across #2a/#2b and should be standardized before adding the larger schedule surface.Recommended next task: do #46 before #36. It is not part of the original #2a–#2e sequence, but it prevents repeating or later rewriting error response metadata across #36 schedules and #37 playouts.
Done
What was done: Completed the REST API tracker across the planned slices and follow-up error contract cleanup:
ProblemDetails./apisweep, 401ProblemDetails, and API-key protection for legacy mutating library/maintenance endpoints.The REST API now routes management mutations through MediatR/EF handlers instead of direct SQLite writes for channels, collections, smart collections, schedules/items, and playouts, with generated OpenAPI coverage for client/MCP use.
Root cause: n/a; this was feature delivery. The original risk being addressed was that direct DB writes bypassed EF TPT mapping, validation, constraints, and WAL safety.
Files changed: Implemented across PRs #44, #45, #47, #50, #51, and #52. Main areas:
ErsatzTV/Controllers/Api,ErsatzTV/Controllers/Api/Requests, relevant MediatR handlers underErsatzTV.Application, shared API result/filter helpers, controller/handler/integration/OpenAPI tests,ErsatzTV/wwwroot/openapi/v1.json, anddocs/rest-api.md.Verification: Each slice merged only after local verification and green PR CI. Final slice #38 / PR #52 passed local LSP diagnostics,
scripts/update-openapi.sh,dotnet build ErsatzTV.sln,TZ=UTC dotnet test ErsatzTV.sln -v normal(263 passed), plus PR CI:Build & test (.NET)andEF migration integrity (SQLite + MySql)both successful.Deferred: CORS remains the existing global
AllowAll; tightening is deferred as an operational exposure decision if write APIs are exposed beyond LAN. Legacy operational route reshaping for library/maintenance actions is deferred to a future operational API cleanup if needed; those mutating endpoints are now covered by API-key write protection.Follow-up issues: none required for the REST API tracker itself. Existing unrelated backlog remains (#39 logo testability, #9 FFmpeg 8 app compatibility, #3 CI/CD audit, Renovate dashboard #22).
Docs updated:
docs/rest-api.mddocuments the final API conventions, completed standardization, CORS review, and deferred items.Closing #2 as complete.
timothy referenced this issue2026-07-17 20:07:13 +02:00