Skip to content

feat(playback): local timeshift buffer with live-edge aware LIVE button - #1201

Open
SalemOurabi wants to merge 12 commits into
4gray:masterfrom
SalemOurabi:local-timeshift-buffer
Open

feat(playback): local timeshift buffer with live-edge aware LIVE button#1201
SalemOurabi wants to merge 12 commits into
4gray:masterfrom
SalemOurabi:local-timeshift-buffer

Conversation

@SalemOurabi

@SalemOurabi SalemOurabi commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Local timeshift buffer (opt-in, Electron only): FFmpeg remuxes M3U, Xtream, and Stalker live-video playback into a bounded sliding HLS buffer served from a token-protected ephemeral 127.0.0.1 endpoint, enabling pause/rewind in Video.js, HTML5/hls.js, ArtPlayer, and Embedded MPV. Radio, VOD/series, catch-up, and external MPV/VLC remain unchanged. Defaults: disabled, 30 min buffer (5–180 min configurable), system temp dir. FFmpeg is detected via FFMPEG_PATH/PATH/known locations; unavailable FFmpeg falls back to the original stream. Sessions are renderer-owned with full cleanup on channel change, destroy, and app shutdown. Architecture doc: docs/architecture/local-timeshift.md.
  • Faster channel zapping while timeshift is active: pending session starts are cancelable during rapid channel changes, plus extended embedded-mpv support.
  • LIVE button in the player controls: the LIVE/timeshift button now lives inside each player's control bar and fades together with the controls. It shows a red dot only while playback is at the live edge and a grey dot when behind; clicking it while behind seeks back to the live edge and resumes playback. Implemented via shared live-edge helpers (getMediaLiveEdge, isMediaAtLiveEdge, observeMediaLiveEdge, seekMediaToLiveEdge) and a LiveEdgeObserver signal wrapper, wired into all four inline players.

Merged with latest master (shared web player controls toggle #1198 and shared picture-in-picture controls #1199); conflict resolution keeps upstream's memoized settings load and combines the timeshift and shared-controls providers.

Validation

  • pnpm nx lint ui-playback services
  • ui-playback unit suites green: player-controls 254, embedded-mpv 121, vjs 71, html 77, art 35, web-player-view 34, timeshift 12 tests (incl. new live-edge regression tests)
  • services settings-store suite and web playback settings spec (26 tests) ✅
  • git diff --check

Test plan

  • Enable Settings > Playback > Local timeshift, play a live channel, pause/rewind, then click LIVE → jumps back to live edge, red dot returns
  • Verify grey dot appears when seeking behind live in Video.js, HTML5, ArtPlayer, Embedded MPV
  • Verify LIVE button fades with the player controls
  • Rapid channel zapping with timeshift enabled → no orphaned FFmpeg processes
  • Radio/VOD/catch-up and external MPV/VLC unaffected
  • Shared PiP + shared-controls toggle from master still work with timeshift active

SalemOurabi and others added 3 commits July 16, 2026 22:45
…port

Release the owner slot synchronously and tear the replaced session down
in the background so rapid channel changes are not serialized behind
FFmpeg termination. Purge leaked public-session entries when a renderer
stops without a session id. Enable seeking and a live-edge action in
embedded MPV while the local buffer is active.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
# Conflicts:
#	libs/ui/playback/src/lib/art-player/art-player.component.html
#	libs/ui/playback/src/lib/art-player/art-player.component.scss
#	libs/ui/playback/src/lib/art-player/art-player.component.spec.ts
#	libs/ui/playback/src/lib/art-player/art-player.component.ts
#	libs/ui/playback/src/lib/vjs-player/vjs-player.component.html
#	libs/ui/playback/src/lib/vjs-player/vjs-player.component.spec.ts
#	libs/ui/playback/src/lib/vjs-player/vjs-player.component.ts
#	libs/ui/playback/src/lib/web-player-view/web-player-view.component.html
#	libs/ui/playback/src/lib/web-player-view/web-player-view.component.shared-controls.spec.ts
#	libs/ui/playback/src/lib/web-player-view/web-player-view.component.spec.ts
@SalemOurabi

Copy link
Copy Markdown
Contributor Author

@greptileai Please review this PR

… state

The LIVE/timeshift button now lives inside each player's control bar and
fades with the controls instead of floating over the video. It shows a red
dot only while playback is at the live edge and a grey dot when behind;
clicking it while behind seeks back to the live edge and resumes playback.

- add shared live-edge helpers (getMediaLiveEdge, isMediaAtLiveEdge,
  observeMediaLiveEdge, seekMediaToLiveEdge) with regression tests
- add LiveEdgeObserver signal wrapper used by Video.js and HTML5 players
- wire live-edge state into ArtPlayer and Embedded MPV (session-based)
- extract vjs error classification and native <video> source helpers to
  keep components within the max-lines lint budget
@greptile-apps

greptile-apps Bot commented Jul 18, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds an opt-in local timeshift buffer for Electron's built-in inline players, a live-edge aware LIVE button that fades with player controls, and a set of shared live-edge helpers. The feature requires FFmpeg and is wired end-to-end through an Electron IPC layer backed by a Node.js HTTP server, with LocalTimeshiftCoordinator owning the renderer-side lifecycle.

  • Local timeshift (Electron only): FFmpeg remuxes live streams into a bounded sliding HLS window served from a loopback token-protected HTTP server; sessions are generation-guarded for rapid channel changes and cleaned up on renderer destroy and app shutdown.
  • LIVE edge button: Shared LiveEdgeObserver/LiveEdgeButtonComponent wired into all four inline players (VJS, HTML5, ArtPlayer, Embedded MPV) via signal-based inputs; clicking seeks back to live edge and resumes playback.
  • Shutdown: before-quit is converted to an async handler that awaits shutdownLocalTimeshift() before calling app.quit(), using shutdownStarted/shutdownComplete flags to guard against re-entrant invocations.

Confidence Score: 5/5

Safe to merge. The timeshift feature is Electron-only, opt-in, and falls back gracefully to the original stream when FFmpeg is absent or a session fails.

The backend implementation uses generation counters and abort signals to prevent stale starts from racing a newer configuration, timing-safe token comparison, and an explicit ffmpegResolved flag that correctly caches not-found results. The renderer-side coordinator properly falls back to the source URL on error. The before-quit async handler is correctly re-entrant-guarded. The only finding is a theoretical key-stability concern around JSON.stringify of a headers object, which is extremely unlikely to trigger given the stable computed-signal chain.

No files require special attention. The new local-timeshift-coordinator.ts is the most complex renderer file but is well-covered by the 202-case spec suite referenced in the PR.

Important Files Changed

Filename Overview
apps/electron-backend/src/app/services/local-timeshift.service.ts New service orchestrating FFmpeg HLS remux sessions; uses explicit ffmpegResolved flag to correctly cache not-found results, generation-based abort for rapid channel changes, and fire-and-forget teardown to avoid blocking zapping.
apps/electron-backend/src/app/services/local-timeshift-http-server.ts Loopback-only HTTP server for HLS segment delivery; uses timing-safe token comparison, strict CORS (loopback + null origin only), whitelist regex for file names, and correct Range request handling.
apps/electron-backend/src/app/events/local-timeshift.events.ts IPC event bridge for timeshift; registers owner cleanup via WeakSet+once('destroyed'), validates start requests, and correctly routes failure notifications back to the owning renderer WebContents.
apps/electron-backend/src/main.ts before-quit converted to an async handler with shutdownStarted/shutdownComplete flags; correctly awaits timeshift shutdown before native-player teardown, with non-blocking database-worker shutdown preserved.
libs/ui/playback/src/lib/timeshift/local-timeshift-coordinator.ts Renderer-side coordinator using generation counters to drop stale starts; caches support probe; falls back to source URL on error; stops previous session before issuing new start.
libs/ui/playback/src/lib/timeshift/live-edge.ts Shared live-edge helpers with correct event-listener cleanup via returned dispose function.
libs/ui/playback/src/lib/web-player-view/web-player-view.component.ts Two-effect architecture correctly guards setChannel() on null playerPlayback(). Minor: JSON.stringify of headers object in coordinator key is order-dependent.
libs/ui/playback/src/lib/vjs-player/vjs-player.component.ts Adds LiveEdgeObserver wired to the native video element; liveEdge.sync() called in ngOnInit and ngOnChanges; ngOnDestroy disconnects observer.
libs/ui/playback/src/lib/html-video-player/html-video-player.component.ts LiveEdgeObserver correctly synced in ngOnInit and ngOnChanges; new localTimeshiftActive uses signal input() while sibling inputs remain @input() decorator.
libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-player.component.ts atLiveEdge computed from session position/duration using the shared tolerance constant; goLive() seeks via MPV controller API.
apps/electron-backend/src/app/services/local-timeshift-ffmpeg.ts FFmpeg discovery via candidate paths with spawnSync probe; header serialization validates RFC 7230 token chars and rejects CR/LF/NUL.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant R as Renderer (Angular)
    participant C as LocalTimeshiftCoordinator
    participant E as Electron IPC (events)
    participant S as LocalTimeshiftService
    participant F as FFmpeg
    participant H as HLS HTTP Server

    R->>C: configure(playback, settings, eligible)
    C->>C: generate key, bump generation
    C->>E: getLocalTimeshiftSupport()
    E->>S: getSupport()
    S-->>E: "{supported: true, engine: 'ffmpeg'}"
    E-->>C: support result
    C->>E: stopLocalTimeshift(prevId?)
    E->>S: stopForOwner(ownerId)
    S->>F: SIGTERM to SIGKILL (graceful)
    C->>E: startLocalTimeshift(request)
    E->>S: start(request)
    S->>F: spawn FFmpeg HLS remux
    S->>H: createLocalTimeshiftHttpServer()
    H-->>S: "{playbackUrl, close()}"
    S->>S: poll for playable playlist
    S-->>E: LocalTimeshiftSession snapshot
    E-->>C: session
    C->>C: "status=ready, playback=timeshiftUrl"
    C-->>R: playerPlayback() updated, player loads timeshift URL

    Note over R,H: User clicks LIVE button
    R->>R: seekMediaToLiveEdge(video)
    R->>R: "media.currentTime = liveEdge - 0.25s"
    R->>R: media.play()

    Note over R,S: Renderer destroyed or channel change
    C->>E: stopLocalTimeshift(sessionId)
    E->>S: stop(sessionId, ownerId)
    S->>F: SIGTERM
    S->>H: close()
    S->>S: rm session directory
Loading
%%{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 (Angular)
    participant C as LocalTimeshiftCoordinator
    participant E as Electron IPC (events)
    participant S as LocalTimeshiftService
    participant F as FFmpeg
    participant H as HLS HTTP Server

    R->>C: configure(playback, settings, eligible)
    C->>C: generate key, bump generation
    C->>E: getLocalTimeshiftSupport()
    E->>S: getSupport()
    S-->>E: "{supported: true, engine: 'ffmpeg'}"
    E-->>C: support result
    C->>E: stopLocalTimeshift(prevId?)
    E->>S: stopForOwner(ownerId)
    S->>F: SIGTERM to SIGKILL (graceful)
    C->>E: startLocalTimeshift(request)
    E->>S: start(request)
    S->>F: spawn FFmpeg HLS remux
    S->>H: createLocalTimeshiftHttpServer()
    H-->>S: "{playbackUrl, close()}"
    S->>S: poll for playable playlist
    S-->>E: LocalTimeshiftSession snapshot
    E-->>C: session
    C->>C: "status=ready, playback=timeshiftUrl"
    C-->>R: playerPlayback() updated, player loads timeshift URL

    Note over R,H: User clicks LIVE button
    R->>R: seekMediaToLiveEdge(video)
    R->>R: "media.currentTime = liveEdge - 0.25s"
    R->>R: media.play()

    Note over R,S: Renderer destroyed or channel change
    C->>E: stopLocalTimeshift(sessionId)
    E->>S: stop(sessionId, ownerId)
    S->>F: SIGTERM
    S->>H: close()
    S->>S: rm session directory
Loading

Reviews (9): Last reviewed commit: "merge: sync latest upstream into timeshi..." | Re-trigger Greptile

Comment thread libs/ui/playback/src/lib/html-video-player/html-video-player.component.ts Outdated
Comment thread libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-player.component.ts Outdated
Comment thread apps/electron-backend/src/app/services/local-timeshift.service.ts
@greptile-apps

greptile-apps Bot commented Jul 18, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds an opt-in local timeshift buffer for Electron's built-in inline players (HTML5/hls.js, Video.js, ArtPlayer, Embedded MPV), plus a live-edge-aware LIVE button across all four players. FFmpeg remuxes the live stream into a bounded sliding HLS playlist served from a token-protected loopback HTTP server, while rapid channel zapping is handled via a generation-based cancellation system.

  • Local timeshift backend: FFmpeg detection, HLS argument building, loopback HTTP server with timing-safe token auth, session lifecycle with owner-scoped cleanup, and an async before-quit shutdown sequence.
  • Frontend coordinator (LocalTimeshiftCoordinator): Angular injectable driving session lifecycle with content-keyed deduplication and generation tracking to prevent stale starts from racing newer channel changes.
  • Live-edge button: Shared helpers (getMediaLiveEdge, isMediaAtLiveEdge, observeMediaLiveEdge, seekMediaToLiveEdge) wired into all four inline players with a pulsing-dot indicator that fades with the controls.

Confidence Score: 4/5

The change is well-structured and safe to merge; the findings are quality concerns rather than runtime failures.

The core FFmpeg and HTTP server path is carefully written with timing-safe token checks, path-traversal guards, header injection protection, and loopback-only binding. The generation-based cancellation logic correctly prevents stale sessions from racing newer channel changes. The async before-quit shutdown sequence is correct. Two quality issues stand out: the ??= assignment in getSupport() does not memoize the undefined case, so resolveFfmpegCommand() is re-run on every new coordinator instance when FFmpeg is absent; and the new localTimeshiftActive input uses the legacy @input() decorator while the rest of the file has migrated to input() signals.

apps/electron-backend/src/app/services/local-timeshift.service.ts (FFmpeg caching), apps/electron-backend/src/app/events/local-timeshift.events.ts (misleading no-op bootstrap method), libs/ui/playback/src/lib/html-video-player/html-video-player.component.ts (legacy @input decorator)

Important Files Changed

Filename Overview
apps/electron-backend/src/app/services/local-timeshift.service.ts Core service orchestrating FFmpeg, HTTP server, and session lifecycle (317 lines — above the 300-line soft limit); ??= caching bug leaves FFmpeg re-probed on every getSupport() call when FFmpeg is absent.
apps/electron-backend/src/app/events/local-timeshift.events.ts IPC event registration; bootstrapLocalTimeshiftEvents() is a no-op since all ipcMain.handle calls are at module scope — misleading for maintainers expecting the bootstrap call to register handlers.
apps/electron-backend/src/app/services/local-timeshift-http-server.ts Loopback-only HTTP server with token auth, timing-safe comparison, path-traversal prevention via ALLOWED_FILE regex, and conservative CORS. Range request handling is correct.
apps/electron-backend/src/app/services/local-timeshift-ffmpeg.ts FFmpeg detection and argument building; header serialisation correctly validates names and rejects injection. Source URL injection prevention via assertSafeSourceUrl is present.
libs/ui/playback/src/lib/timeshift/local-timeshift-coordinator.ts Angular coordinator managing timeshift lifecycle with generation-based cancellation for rapid channel zapping; supportPromise caching and concurrent stop/start ordering appear correct.
libs/ui/playback/src/lib/html-video-player/html-video-player.component.ts New localTimeshiftActive input added using legacy @Input() decorator while the same file already uses input() signals for other inputs, violating CLAUDE.md Angular standards.
apps/electron-backend/src/main.ts Before-quit handler refactored to async sequential shutdown; double-quit guard via shutdownComplete/shutdownStarted flags is correct.
libs/ui/playback/src/lib/timeshift/live-edge.ts Shared live-edge helpers are clean, well-tested, and correctly derived from the seekable range.
apps/electron-backend/src/app/services/local-timeshift-playlist.ts Playlist readiness polling and buffer metrics; abortable delay correctly cleans up on abort signal and the readiness check validates both playlist structure and segment file existence.
libs/ui/playback/src/lib/web-player-view/web-player-view.component.ts Coordinator wired into two effects with generation-safe ordering; playerPlayback null-guard defers channel update until the coordinator publishes the timeshift URL.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant R as Renderer
    participant C as LocalTimeshiftCoordinator
    participant P as Preload Bridge
    participant M as Main Process
    participant S as LocalTimeshiftService
    participant F as FFmpeg
    participant H as Loopback HTTP Server

    R->>C: configure(playback, settings, eligible)
    C->>C: "increment generation, status=starting"
    C->>P: getLocalTimeshiftSupport()
    P->>M: IPC GET_SUPPORT
    M->>S: getSupport()
    S-->>C: "supported=true"
    C->>P: startLocalTimeshift(request)
    P->>M: IPC START
    M->>S: start(request)
    S->>S: mkdtemp() session directory
    S->>H: createHttpServer(dir, token)
    H-->>S: playbackUrl
    S->>F: spawn ffmpeg -i src -f hls ...
    S->>S: poll for playable playlist
    S-->>C: LocalTimeshiftSession
    C->>C: "status=ready, playback=timeshiftUrl"
    C-->>R: playerPlayback() emits timeshiftUrl
    R->>R: setChannel(timeshiftUrl)

    Note over R,H: User rewinds, LIVE button shows grey dot
    R->>R: goLive() seekMediaToLiveEdge(media)

    Note over R,M: Channel change or destroy
    R->>C: configure(newPlayback) or onDestroy
    C->>P: stopLocalTimeshift(sessionId)
    P->>M: IPC STOP
    M->>S: cleanupSession
    S->>F: SIGTERM then SIGKILL
    S->>H: close()
    S->>S: rm -rf session directory
Loading
%%{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 C as LocalTimeshiftCoordinator
    participant P as Preload Bridge
    participant M as Main Process
    participant S as LocalTimeshiftService
    participant F as FFmpeg
    participant H as Loopback HTTP Server

    R->>C: configure(playback, settings, eligible)
    C->>C: "increment generation, status=starting"
    C->>P: getLocalTimeshiftSupport()
    P->>M: IPC GET_SUPPORT
    M->>S: getSupport()
    S-->>C: "supported=true"
    C->>P: startLocalTimeshift(request)
    P->>M: IPC START
    M->>S: start(request)
    S->>S: mkdtemp() session directory
    S->>H: createHttpServer(dir, token)
    H-->>S: playbackUrl
    S->>F: spawn ffmpeg -i src -f hls ...
    S->>S: poll for playable playlist
    S-->>C: LocalTimeshiftSession
    C->>C: "status=ready, playback=timeshiftUrl"
    C-->>R: playerPlayback() emits timeshiftUrl
    R->>R: setChannel(timeshiftUrl)

    Note over R,H: User rewinds, LIVE button shows grey dot
    R->>R: goLive() seekMediaToLiveEdge(media)

    Note over R,M: Channel change or destroy
    R->>C: configure(newPlayback) or onDestroy
    C->>P: stopLocalTimeshift(sessionId)
    P->>M: IPC STOP
    M->>S: cleanupSession
    S->>F: SIGTERM then SIGKILL
    S->>H: close()
    S->>S: rm -rf session directory
Loading
Prompt To Fix All With AI
Fix the following 4 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 4
apps/electron-backend/src/app/services/local-timeshift.service.ts:96-100
**FFmpeg "not found" result is never cached**

`??=` only skips the RHS when the LHS is non-nullish. When `resolveFfmpegCommand()` returns `undefined` (FFmpeg absent), `this.ffmpegCommand` is assigned `undefined` and the LHS stays nullish on every subsequent call, so `resolveFfmpegCommand()` — which spawns a subprocess for each candidate path — is invoked again on every new `getSupport()` call. A new `LocalTimeshiftCoordinator` instance (created on every navigation to the player view) triggers a fresh IPC probe, which re-runs the spawns. A sentinel flag would pin the result after the first probe.

### Issue 2 of 4
libs/ui/playback/src/lib/html-video-player/html-video-player.component.ts:72
**Legacy `@Input()` decorator for new input — CLAUDE.md requires `input()` signal**

`channel`, `volume`, `startTime`, and `seriesNavigation` are pre-existing legacy inputs; CLAUDE.md's Angular coding standards say new inputs must use `input()`. The new `localTimeshiftActive` should follow the same pattern as `isLive` and `interactionEnabled` already in this file.

```suggestion
    readonly localTimeshiftActive = input(false);
```

### Issue 3 of 4
apps/electron-backend/src/app/events/local-timeshift.events.ts:43-47
**`bootstrapLocalTimeshiftEvents()` is a no-op — IPC handlers are already registered at module load time**

All three `ipcMain.handle()` calls (lines 49, 51, 70) and `service.setFailureHandler()` run at the top level when the module is first imported. The static bootstrap method just returns `ipcMain` without doing anything. Other event classes register their handlers inside the bootstrap method; this class breaks that pattern.

### Issue 4 of 4
apps/electron-backend/src/app/services/local-timeshift.service.ts:68
**`??=` leaves `ffmpegCommand` permanently `undefined` when FFmpeg is absent**

Replace the nullish-coalescing assignment with an explicit first-probe flag so the `undefined` result is remembered across calls.

```suggestion
    private ffmpegCommand?: string;
    private ffmpegResolved = false;
```

Reviews (2): Last reviewed commit: "feat(timeshift): move LIVE button into p..." | Re-trigger Greptile

Comment thread apps/electron-backend/src/app/services/local-timeshift.service.ts
Comment thread libs/ui/playback/src/lib/html-video-player/html-video-player.component.ts Outdated
Comment thread apps/electron-backend/src/app/events/local-timeshift.events.ts Outdated
Comment thread apps/electron-backend/src/app/services/local-timeshift.service.ts
…uffer

Resolves conflicts with the shared web player controls toggle (4gray#1198) and
shared picture-in-picture controls (4gray#1199): keeps the memoized settings
load and webPlayerSharedControls handling from upstream while restoring
localTimeshift normalization, combines the LocalTimeshiftCoordinator and
WEB_PLAYER_SHARED_CONTROLS providers, and adopts the queryByTestId helper
in the playback settings spec. Extracts embedded-mpv adapter spec fixtures
into spec-helpers to stay within the max-lines lint budget.
@SalemOurabi
SalemOurabi force-pushed the local-timeshift-buffer branch from 83e0c67 to 5e290ca Compare July 18, 2026 05:20
… service

- convert localTimeshiftActive to a signal input in the HTML5 player
- reuse DEFAULT_LIVE_EDGE_TOLERANCE_SECONDS in the embedded MPV live-edge check
- cache a missing FFmpeg probe result instead of re-probing on every call
- register local-timeshift IPC handlers in bootstrapLocalTimeshiftEvents()
- extract session start orchestration into LocalTimeshiftSessionStarter
@SalemOurabi

Copy link
Copy Markdown
Contributor Author

@greptileai Please review the latest commit 645f343 — it addresses all previous review comments (signal input, shared live-edge tolerance constant, FFmpeg probe caching, IPC bootstrap registration, and the service split via LocalTimeshiftSessionStarter).

@SalemOurabi

Copy link
Copy Markdown
Contributor Author

@greptileai Please also include d3044a9 in the review — a CI test fix adding the missing webPlayerSharedControls form control to the local-timeshift settings spec.

@SalemOurabi

Copy link
Copy Markdown
Contributor Author

@greptileai Please also review 9f2d213 — CSS fix raising the LIVE button above ArtPlayer's vendor bottom bar (progress + controls stack) while keeping the shared-controls offset aligned with the HTML5 player.

@codecov-commenter

codecov-commenter commented Jul 18, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 75.15991% with 233 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.58%. Comparing base (e91cbd5) to head (b6b8624).
⚠️ Report is 2020 commits behind head on master.

Files with missing lines Patch % Lines
...nd/src/app/services/local-timeshift-http-server.ts 44.44% 68 Missing and 2 partials ⚠️
apps/electron-backend/src/main.ts 8.00% 23 Missing ⚠️
...ackend/src/app/services/local-timeshift-process.ts 62.74% 11 Missing and 8 partials ⚠️
...ackend/src/app/services/local-timeshift.service.ts 84.29% 9 Missing and 10 partials ⚠️
...k/src/lib/timeshift/local-timeshift-coordinator.ts 80.00% 11 Missing and 7 partials ⚠️
...n-backend/src/app/events/local-timeshift.events.ts 81.60% 11 Missing and 5 partials ⚠️
...k/src/lib/web-player-view/web-player-view.utils.ts 64.70% 7 Missing and 5 partials ⚠️
...backend/src/app/services/local-timeshift-ffmpeg.ts 83.05% 5 Missing and 5 partials ⚠️
...bedded-mpv-player/embedded-mpv-player.component.ts 52.38% 4 Missing and 6 partials ⚠️
...ckend/src/app/services/local-timeshift-playlist.ts 80.85% 4 Missing and 5 partials ⚠️
... and 12 more
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

❗ There is a different number of reports uploaded between BASE (e91cbd5) and HEAD (b6b8624). Click for more details.

HEAD has 4 uploads less than BASE
Flag BASE (e91cbd5) HEAD (b6b8624)
4 0
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1201      +/-   ##
==========================================
- Coverage   71.05%   63.58%   -7.47%     
==========================================
  Files          40      688     +648     
  Lines         691    40453   +39762     
  Branches       87     8857    +8770     
==========================================
+ Hits          491    25723   +25232     
- Misses        176    11448   +11272     
- Partials       24     3282    +3258     
Flag Coverage Δ
unit 63.58% <75.15%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor Author

@greptile please re-review the updated Timeshift branch. It now includes current upstream, resolves the Electron shutdown and settings-test conflicts, and carries the shared EPG schema split required by the current lint policy. Validation: all 41 Nx lint projects pass, Electron backend TypeScript passes, database build passes, 22 database tests pass, and the Timeshift/playback validation remains green (705 tests across the affected suites).

@SalemOurabi

Copy link
Copy Markdown
Contributor Author

@greptileai Please review this pull request

@SalemOurabi

Copy link
Copy Markdown
Contributor Author

@greptileai Please review this pull request

# Conflicts:
#	libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-player.component.html
@SalemOurabi

Copy link
Copy Markdown
Contributor Author

@greptile please re-review after the latest upstream sync and semantic conflict resolution. The Timeshift behavior and the new upstream MPV/EPG behavior were preserved together; targeted unit tests, formatting, diff checks, and affected-project lint all pass locally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants