feat(recording): add DVR scheduler and recording library - #1190
feat(recording): add DVR scheduler and recording library#1190SalemOurabi wants to merge 13 commits into
Conversation
|
@greptileai Please review this pull request thoroughly, with particular attention to DVR scheduling and recovery, SQLite migration and worker ordering, IPC data exposure, recording-engine lifecycle and cancellation, VLC credential/header handling, playlist-deletion races, and recording-library UI regressions. |
Greptile SummaryThis PR adds a complete Electron-only DVR feature: a recording scheduler with lifecycle persistence, dual engine support (embedded MPV primary, headless VLC fallback), EPG-triggered recording actions across M3U/Xtream/Stalker views, a routed recording library UI, database migration, and IPC/preload wiring. All four bugs flagged in the previous review pass have been addressed:
The overall architecture — exclusive-operation queues per recording ID, serialized playlist-delete coordination via the scheduling gate, hardware power-save blocker management, and startup recovery — holds up under detailed tracing of concurrent paths. Confidence Score: 5/5Safe to merge after the sidebar overlap with #1185 is resolved — the core DVR logic is solid and all previously-identified defects have been corrected. Every concurrency edge case in the scheduler (terminal-state races, shutdown sequencing, lock-free timer re-arm, and exclusive-operation chaining) traces correctly. The one remaining comment is a cosmetic listener-cleanup suggestion in waitForProcessSpawn with no runtime impact. No regressions were introduced in the EPG or playlist-delete integration paths. No files require special attention beyond the sidebar/recording-library overlap with PR #1185 noted in the PR description. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant R as Renderer
participant P as Preload (IPC)
participant S as RecordingSchedulerService
participant G as RecordingSchedulingGate
participant RT as RecordingSchedulerRuntime
participant E as DesktopRecordingEngine
participant DB as DatabaseWorker
R->>P: recordingsSchedule(request)
P->>S: schedule(request)
S->>G: runForPlaylist(playlistId, ...)
G->>RT: runExclusive(scheduleKey, ...)
RT->>DB: create(id, request)
RT->>RT: armRecording(recording)
Note over RT: Sets start/end timers
RT-->>S: recording created
S-->>R: success + recording
Note over RT: Start timer fires
RT->>RT: runExclusive(recordingId, startRecording)
RT->>DB: update status to recording
RT->>E: start(recording)
E->>E: selectAvailableEngine MPV or VLC
E-->>RT: fileName, filePath, bytesRecorded
RT->>DB: update engineResult
Note over RT: Sets end timer
Note over RT: End timer fires
RT->>RT: runExclusive(recordingId, finishRecording)
RT->>E: stop(recordingId)
E-->>RT: fileName, filePath, bytesRecorded
RT->>DB: update status to completed
RT->>P: broadcastRecordingUpdate
P->>R: onRecordingsUpdate event
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant R as Renderer
participant P as Preload (IPC)
participant S as RecordingSchedulerService
participant G as RecordingSchedulingGate
participant RT as RecordingSchedulerRuntime
participant E as DesktopRecordingEngine
participant DB as DatabaseWorker
R->>P: recordingsSchedule(request)
P->>S: schedule(request)
S->>G: runForPlaylist(playlistId, ...)
G->>RT: runExclusive(scheduleKey, ...)
RT->>DB: create(id, request)
RT->>RT: armRecording(recording)
Note over RT: Sets start/end timers
RT-->>S: recording created
S-->>R: success + recording
Note over RT: Start timer fires
RT->>RT: runExclusive(recordingId, startRecording)
RT->>DB: update status to recording
RT->>E: start(recording)
E->>E: selectAvailableEngine MPV or VLC
E-->>RT: fileName, filePath, bytesRecorded
RT->>DB: update engineResult
Note over RT: Sets end timer
Note over RT: End timer fires
RT->>RT: runExclusive(recordingId, finishRecording)
RT->>E: stop(recordingId)
E-->>RT: fileName, filePath, bytesRecorded
RT->>DB: update status to completed
RT->>P: broadcastRecordingUpdate
P->>R: onRecordingsUpdate event
Reviews (9): Last reviewed commit: "merge: sync latest upstream into DVR" | Re-trigger Greptile |
|
@greptileai please re-review the latest DVR follow-up, especially playlist-cancel races, best-effort shutdown, async file availability checks, VLC lifecycle tracking, and the CI/E2E fixes. |
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1190 +/- ##
==========================================
- Coverage 71.05% 63.65% -7.41%
==========================================
Files 40 704 +664
Lines 691 41123 +40432
Branches 87 8977 +8890
==========================================
+ Hits 491 26177 +25686
- Misses 176 11542 +11366
- Partials 24 3404 +3380
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@greptileai please re-review the latest Linux E2E follow-up. The SQLite migration fixture now receives its mode and paths through explicit test environment variables, so Chromium switches cannot shift positional arguments. |
|
@greptileai please re-review the latest bulk-cancellation race fix. |
|
@greptile-apps Please re-review the latest conflict-resolution merge. It preserves upstream's embedded-MPV frame-copy and shared player-control changes while keeping hidden main-process DVR sessions private and alive across renderer reloads/crashes. Regression coverage was added for that lifecycle boundary, and the affected tests, lint, typecheck, and Electron build pass. |
# Conflicts: # CLAUDE.md # apps/electron-backend/src/app/database/schema.ts # apps/electron-backend/src/app/services/debug-trace.spec.ts # apps/electron-backend/src/app/services/embedded-mpv-native.service.spec.ts # apps/electron-backend/src/app/services/embedded-mpv-native.service.ts # apps/electron-backend/src/main.ts
|
@greptile please re-review the latest DVR update. The failing CI lint was reproduced locally and fixed in d5f802e by extracting the EPG channel-mapping schema without changing its public exports or table contract. Validation: the exact 43-project CI lint command passes, |
Adopts the upstream EPG mapping schema split (epg-mapping.schema.ts) and drops the branch-local duplicate module so the table is defined once.
|
@greptileai Please review this pull request |
# Conflicts: # libs/portal/stalker/feature/src/lib/stalker-live-stream-layout/stalker-live-stream-layout.component.ts
|
@greptile please re-review after the latest upstream sync and semantic conflict resolution. The DVR behavior and the new upstream MPV/EPG behavior were preserved together; targeted unit tests, formatting, diff checks, and affected-project lint all pass locally. |
What changed
Security and behavior
Validation
git diff --checkpassedDocumentation
Updated
README.md,CLAUDE.md, and the DVR, embedded-MPV, Nx-boundary, SQLite-worker, and workspace-shell architecture documentation.Dependency
This branch contains the expandable sidebar integration needed by the Recordings route and therefore overlaps with #1185. Rebase the DVR branch after #1185 merges to drop the duplicate sidebar commit before merging this PR.