mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-11 11:06:16 -08:00
fix(playback): clarify external player launch feedback (#1388)
This commit is contained in:
1 parent
8442747c37
commit
d73acd6bfc
101 files changed
+8819
-850
No files matched your search
@@ -582,12 +582,13 @@ offer a same-family built-in alternative. Unknown source or engine-family facts
|
||||
also suppress built-in recommendations.
|
||||
|
||||
The pure policy builds the following order, filters the current, unavailable,
|
||||
incompatible, and already attempted targets, and then returns at most three
|
||||
actions. It also projects attempted inline target IDs through the validated
|
||||
incompatible, and already attempted inline targets, and then returns at most
|
||||
three actions. It projects attempted inline target IDs through the validated
|
||||
canonical source/target capability matrix and filters every engine family that
|
||||
has already been attempted. HTML5 and ArtPlayer therefore cannot be offered as
|
||||
separate hls.js recovery attempts. The first surviving action is primary and
|
||||
later actions are secondary.
|
||||
separate hls.js recovery attempts. External MPV/VLC attempts remain eligible;
|
||||
the view reranks them by per-target attempt count instead of removing them. The
|
||||
first surviving action is primary and later actions are secondary.
|
||||
|
||||
| Sanitized evidence | Candidate order |
|
||||
| ----------------------------------------- | ------------------------------------------------------------ |
|
||||
@@ -654,8 +655,93 @@ override and saved player setting. The new engine receives the latest finite
|
||||
VOD position as a best-effort resume point; live playback starts at the live
|
||||
edge. Retry reloads the active target without clearing attempts. Selecting MPV
|
||||
or VLC records the external target before emitting the existing fallback
|
||||
request. The system does not infer whether the external process ultimately
|
||||
played the stream.
|
||||
request. Both external actions remain mounted while their per-target state
|
||||
moves through `launching`, `started`, `playing`, or `error`; an attempted idle
|
||||
target becomes an explicit reopen action and a failed target becomes Try again.
|
||||
Only an exact correlated Electron `playing` session update earns the Playing
|
||||
label. A single external launch handshake owns the session: duplicate actions
|
||||
are ignored, other external actions wait, and an existing live external session
|
||||
must close before a different player can start. Play, Restart, and secondary
|
||||
provider launch actions all observe the same local guard before Electron has
|
||||
returned a session. Xtream VOD also records the diagnostic fallback's
|
||||
route-scoped destination and pending generation before invoking MPV/VLC, so a
|
||||
subsequent route cannot start a second detached player while the first launch
|
||||
is being correlated. The source-owning host binds
|
||||
its returned launch promise to the fieldless current intent, so only that exact
|
||||
result supplies the initial session ID; a late result from a timed-out attempt
|
||||
cannot take over a retry. Later global updates must match the exact ID. A
|
||||
replacement does not launch until teardown of the exact spawned process is
|
||||
confirmed, and the old target is settled synchronously before the new launch so
|
||||
coalesced signal effects cannot preserve stale feedback. Teardown waits through
|
||||
bounded graceful and forced-exit windows, and reusable MPV also bounds its IPC
|
||||
quit command before entering those windows. If any stage cannot confirm exit,
|
||||
close rejects and keeps the exact session live so a replacement cannot overlap
|
||||
it. A process-wide teardown gate starts before any potentially slow teardown
|
||||
preparation, including VLC position flush and a reused player's MPV IPC or VLC
|
||||
RC quit command, and rejects both player launches until that exact child
|
||||
reports exit; `ChildProcess.killed` is never treated as proof. If
|
||||
bounded teardown fails while a fresh launch IPC is pending, the IPC rejects and
|
||||
the exact session becomes a closable error instead of remaining in Opening.
|
||||
If a pre-content reuse failure has no still-live displaced session to restore,
|
||||
the replacement error keeps its attached closer so Stop can retry teardown of
|
||||
the orphaned reusable child. A terminal error without a closer is never
|
||||
restorable.
|
||||
A rejected close promise is cached only while that attempt is pending, so the
|
||||
dock's Stop action can retry teardown of the same exact child after an
|
||||
unconfirmed bounded attempt. Reusable children are mapped to the current
|
||||
content session; a stale older closer becomes a no-op after MPV `loadfile` or
|
||||
the VLC enqueue handoff remaps the process. Closing an already terminal session
|
||||
is also idempotent: it returns the closed snapshot without invoking the saved
|
||||
closer, and a later process error cannot revive it as a visible failure. Reused
|
||||
MPV commands use the socket captured for their exact child, so a delayed close
|
||||
cannot send `quit` to a replacement process through a newer global socket.
|
||||
If Stop is observed before a pending MPV content command or VLC enqueue command
|
||||
is dispatched, that command is skipped. A source handoff also fails closed
|
||||
while a live session has no closer (`canClose: false`); renderer Dismiss is not
|
||||
accepted as process-teardown confirmation. That denial advances neither the
|
||||
multi-source switch token nor the playback generation, so it cannot cancel the
|
||||
sole launch already in flight.
|
||||
VLC rechecks that gate immediately around every concrete spawn after
|
||||
asynchronous port allocation or reuse fallback work. If a post-start VLC
|
||||
fallback is blocked by the gate, the already-opened session transitions to
|
||||
error instead of continuing to claim that the player started. If RC-port
|
||||
allocation fails, reuse ownership is never claimed: the spawned VLC child
|
||||
keeps its exact one-shot session closer so Stop still confirms its teardown.
|
||||
During reusable-player handoff, a failure before the content command restores
|
||||
the globally displaced renderer session—not the reusable process's prior
|
||||
owner—through an exact `restoredFromSessionId` transition, but only while that
|
||||
displaced replacement is still the active session.
|
||||
After MPV `loadfile` or VLC `clear` has been dispatched, the attempted session
|
||||
owns the possibly changed process and stays a closable error instead of
|
||||
restoring stale content metadata. Such an error still participates in every
|
||||
replacement close. Stop during an in-flight MPV or VLC reuse command, including
|
||||
the teardown wait after a failed command and VLC's subsequent fallback
|
||||
port-allocation wait, settles that exact close and cancels the fallback spawn.
|
||||
If a partially applied reuse
|
||||
command is instead recovered by a fresh spawn, the old child's exit is retired
|
||||
under its previous session so it cannot close the replacement session. A
|
||||
during-start Stop also settles VLC's launch IPC when a spawn error reports
|
||||
`close` without `exit`. The dock keeps Stop as the only global teardown action
|
||||
while that exact closer remains live; safe Dismiss is available only after an
|
||||
error becomes terminal and has no closer. If diagnostic ownership changes during close, the
|
||||
unlaunched intent is cancelled without a false launch error; an exact stale
|
||||
launch whose close fails keeps its credential-free owner for the next close
|
||||
attempt. Route Play/Resume cancels older source resolution immediately but
|
||||
captures the initiating playlist/VOD route before awaiting teardown; navigation
|
||||
cancels that start. Accepting a diagnostic fallback cancels the same older
|
||||
source resolution before opening MPV/VLC, and a fallback resolving on the new
|
||||
route closes its exact returned session. The route commits its source badge and playback
|
||||
evidence only after start succeeds. If the local
|
||||
handshake timeout fires after Electron has supplied an exact session ID, the ID
|
||||
stays correlated so a later `opened`, `playing`, or `error` update can reconcile
|
||||
the action with the global dock.
|
||||
|
||||
The global external-playback dock uses the same Electron session status. It
|
||||
shows Opening player with progress during launch, Player started for `opened`,
|
||||
and Playing only for `playing`. An error with a live closer remains visible with
|
||||
Stop until teardown is confirmed; an unclosable terminal error remains visible
|
||||
until dismissed. The dock deliberately has no retry because it does not own the
|
||||
original headers or credentials required to reconstruct a safe launch request.
|
||||
|
||||
No recommendation mutates `Settings.player` or another persisted setting.
|
||||
Recovery recommendations never auto-switch a player or source and do not
|
||||
@@ -709,14 +795,18 @@ External MPV/VLC integration is split across focused main-process modules:
|
||||
- `apps/electron-backend/src/app/events/external-player-runtime.ts` owns shared
|
||||
session tracking, trace logging, renderer notifications, playback-position
|
||||
forwarding, and user-facing start errors.
|
||||
- `apps/electron-backend/src/app/events/mpv-session.service.ts` owns MPV process,
|
||||
socket, reuse, cleanup, and progress polling lifecycle.
|
||||
- `apps/electron-backend/src/app/events/vlc-session.service.ts` owns VLC process,
|
||||
RC interface, reuse, cleanup, command parsing, and progress polling lifecycle.
|
||||
- `apps/electron-backend/src/app/events/mpv-session.service.ts` owns fresh MPV
|
||||
launches and progress polling; `mpv-reusable-process.ts` owns the tracked
|
||||
child, captured socket, remapping, retryable close, and reuse handoff.
|
||||
- `apps/electron-backend/src/app/events/vlc-session.service.ts` owns fresh VLC
|
||||
launches and progress polling; `vlc-reusable-process.ts` owns the tracked
|
||||
child, RC-port remapping, retryable close, and reuse handoff, while
|
||||
`vlc-rc.ts` owns bounded RC commands, parsing, and playback snapshots.
|
||||
|
||||
Keep player-specific process state in the MPV/VLC session modules. Shared spawn,
|
||||
request-header, session-registry, and notification helpers belong in the
|
||||
`external-player-*` modules so IPC registration stays small and reviewable.
|
||||
Keep player-specific reusable-process state in the focused MPV/VLC managers.
|
||||
Shared spawn, request-header, session-registry, and notification helpers belong
|
||||
in the `external-player-*` modules so IPC registration stays small and
|
||||
reviewable.
|
||||
|
||||
## Flatpak External Players
|
||||
|
||||
|
||||
@@ -496,8 +496,9 @@ cut. The toast offers Undo, and adds a dub warning when
|
||||
audio track as fact. Two guesses, or a guess against a fact, stay silent.
|
||||
|
||||
Web engines only (HTML5/hls.js, Video.js, ArtPlayer). Embedded MPV suppresses
|
||||
shared diagnostics and owns its own error block; external MPV/VLC are
|
||||
fire-and-forget with no error channel back.
|
||||
shared diagnostics and owns its own error block. External MPV/VLC use managed
|
||||
Electron sessions: recovery actions and the global dock report their exact
|
||||
launch state without retaining playback headers or credentials.
|
||||
|
||||
## External players and an alternative source
|
||||
|
||||
@@ -507,7 +508,43 @@ that session carries the OTHER playlist's ids. `matchedExternalPlayback` would
|
||||
disown it: the primary button never became Stop, stopping found no session, and
|
||||
another click opened a second player. The page therefore claims a session that
|
||||
matches either the route's own stream or the alternative multi-source says is
|
||||
active (`VodDetailsPlaybackBindings.activeSource`).
|
||||
active (`VodDetailsPlaybackBindings.activeSource`). It also claims the
|
||||
credential-free destination identity before awaiting the Electron launch: the
|
||||
primary action becomes pending immediately, and a launching or closable-error
|
||||
session remains matched before the controller can truthfully mark it active.
|
||||
Restart and the provider-source shortcut read the same local pending state, so
|
||||
a second activation cannot enter the Electron launch before the first IPC
|
||||
response exists; their DOM controls stay mounted and disabled to preserve focus.
|
||||
The diagnostic-fallback handler makes that same route-scoped ownership claim
|
||||
before invoking MPV/VLC, rather than relying on the later controller commit. It
|
||||
also supersedes an older source resolution at that acceptance boundary, so the
|
||||
late switch cannot close and replace the newer diagnostic fallback.
|
||||
That retained destination is scoped to the initiating playlist/VOD route key,
|
||||
so Angular route reuse for a different movie cannot turn its Play action into
|
||||
Stop for the previous movie's still-running external session. That new route's
|
||||
Play and Resume actions still pass through the shared close-before-replacement
|
||||
path, and they recheck their captured route key after teardown, so starting it
|
||||
cannot leave the prior detached player running or launch stale content after
|
||||
navigation. A diagnostic fallback that resolves after route reuse closes its
|
||||
exact returned session instead of adopting it on the new route.
|
||||
|
||||
Before an external alternative replaces another external session, teardown of
|
||||
the exact old process must be confirmed. If close fails or times out, the
|
||||
replacement is cancelled instead of allowing two external players to overlap.
|
||||
If the old launch is still opening and has no exact closer yet, a source-row
|
||||
replacement is denied before either the multi-source switch token or playback
|
||||
generation advances; the only in-flight launch therefore remains owned and is
|
||||
not closed as superseded.
|
||||
Once that old session is closed, later duplicate Stop/Close delivery is a
|
||||
terminal no-op and cannot invoke its saved closer against a newly started MPV
|
||||
or VLC process.
|
||||
The controller commits the destination row, previous-source pointer, and switch
|
||||
notice only after the playback seam accepts that handoff and an external
|
||||
Electron launch resolves, so a rejected close or launch leaves the old source
|
||||
as the truthful selection. The host's exact switch-owner probe crosses that
|
||||
seam too: after teardown but before applying playback, the route rechecks it so
|
||||
a newer unresolvable selection cannot leave the older player launched but
|
||||
disowned by the controller.
|
||||
|
||||
`ownsContent()` answers that question once, for both consumers: the session
|
||||
matcher AND the playback-position bridge. They cannot be allowed to disagree —
|
||||
@@ -609,10 +646,22 @@ honest state, and the same one the rest of this feature takes when it does not
|
||||
know.
|
||||
|
||||
Switching sources through `startResolvedPlayback` closes the external session
|
||||
it LAUNCHED first — tracked separately from the controller's active source,
|
||||
which a switch has already moved to the destination by then. It REPLACES what is playing — with MPV or VLC and instance
|
||||
reuse off, the backend would otherwise spawn a second detached player, leaving
|
||||
both sources running and Stop owning only the newer one.
|
||||
it LAUNCHED first — tracked separately so refreshes and overlapping handoffs
|
||||
cannot disown it within the same route session. The retained identity is ignored
|
||||
after the playlist/VOD route key changes. Only after that exact teardown, the
|
||||
host ownership checks on both sides of the launch await, and an `opened` or
|
||||
`playing` launch result does the controller commit the destination. A launch
|
||||
that loses ownership while IPC is pending is closed by its exact returned
|
||||
session; a Stop that wins the race returns `closed` and is never committed. A
|
||||
partial reusable-player handoff that leaves a closable error retains its
|
||||
destination identity so the next source switch closes that exact process before
|
||||
trying again. A stale launch whose exact close fails retains the same
|
||||
credential-free identity for another close attempt. Direct route Play/Resume
|
||||
supersede an older source resolution before waiting for teardown, but update the
|
||||
active-source badge, playback evidence, and controller position only after the
|
||||
new start succeeds. With MPV or VLC and instance reuse off, applying playback
|
||||
earlier would spawn a second detached player, leaving both sources running and
|
||||
Stop owning only the newer one.
|
||||
|
||||
## Short titles and Unicode
|
||||
|
||||
|
||||
Reference in new issue
Block a user