A single pending-reset slot held only the latest reset, so resetting A,
moving on to B and resetting it, then coming back to A before A's write
landed freed A's starts. Pending resets are a list now; a start is held
while any of them names the copy the page acts on, and each reset
removes only its own entry when it settles.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The reset-in-flight flag was page-wide, so following Similar to another
movie before the write landed held that movie's Play and launches too.
The pending reset now names the copy it clears and blocks a start only
while the page's actions act on that same copy, so the reused page can
start another movie, and the original is held again if the viewer
comes back before the write landed.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Between the reset's click and its write landing, the effective position
stayed populated and the primary, provider, Start over and external
actions stayed enabled, so a start made then resumed from the row being
cleared. The playback service tracks the reset in flight and refuses a
start meanwhile; the hero's actions and the menu rows are held for the
same window.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Both pages still projected the legacy tag and cast markup whose styles
the redesign removed, so chips rendered as plain text and cast photos at
their intrinsic size. The tags are meta chips now, the offline cast is
the shared credits line plus the cast row (plain entries, there is no
actor page offline), and each hero is keyed by the download or
recording id so two entries sharing a title decide their layout apart.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The completed quick start is a disabled label naming the last episode,
yet both series menus still offered to launch it externally or copy its
URL. The rows and their handlers now require a startable quick start.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Reset progress was offered on the route copy's row and cleared only
that row, so with a pinned alternative the visible pinned progress
stayed and the next Play still resumed it. The row now follows the
effective position the primary button describes, and the reset clears
the pinned copy's own row, forgetting its loaded position so a lookup
in flight cannot bring it back.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Restart and the menu's MPV/VLC launch fell through to the route's copy
while the pinned copy was playing, since a playing pin counts as
nothing to honour for plain Play. Both now ask the pinned play to
replace the running copy. The hero also reads the position the primary
button acts on, so its progress and remaining time describe the pinned
copy rather than the route's row.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The menu resolved the route's copy while reading the resume flag of the
copy the primary button acts on, so with a pin it could launch a
different source from Play and Start over and apply the wrong resume
decision. The launch is host-owned now: a pinned copy plays through the
pinned-source orchestration with the forced player and its own resume
point, otherwise the route copy starts from its own position.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The external-player and Start over rows stayed enabled while a start
was resolving or a managed MPV/VLC launch still awaited Electron, where
another start is refused: selecting them closed the menu and did
nothing. Both rows are disabled during that window, as the hero's
primary action already is.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The downloaded movie's provider action stays enabled while a start was
pending, so a second press could start beside a launch already inside
the player IPC. The series menu's reset row stayed enabled while a
watched batch persisted, although the hosts refuse the reset meanwhile.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The launch settlement swallowed a rejected launch together with a
superseded one, so the menu's launch had nothing to report. It now hands
a failure that nothing superseded to the page, which logs it and shows
the playback error.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The queued entry stays in place while the player the launch opened
closes, so a choice made meanwhile replaces it instead of being stranded
(Stalker) or registering a second replay that starts beside the first
(Xtream). The series launch helpers log through the shared redacting
logger.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Replaying a choice held during a forced MPV/VLC launch closed nothing, so
with the embedded player configured the inline player mounted next to
the external player that had just opened. Both portals now close the
session the launch opened before the choice starts, keep the series
pending while that player closes, and drop the choice when the player
has to stay or the page moved on.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The held episode choices live in a per-series queue: the series view is
reused across series, so a launch of one series settling no longer drops
what a viewer chose for another series whose launch is still in flight.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The forced MPV/VLC launch IPC cannot be cancelled by a later generation,
so an episode chosen while it runs now waits for it and plays once it
settles, instead of opening a second player next to the first. The hero
button and the external-player menu row are held while a start is
pending, in both portals.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The token carries a module-wide instance number next to the visit
counter, so a page that replaced a destroyed one cannot collide with the
launches that page left behind.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The duplicate guard of forced episode launches is keyed by the page
visit, not the owner, so a launch left behind by an earlier visit of the
same series no longer swallows the reopened page's click; that click
queues behind the stale launch on the owner's chain instead.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The menu compares the playback service's series+visit page token, so a
launch started on an earlier visit of the same series does not surface
its failure after the viewer came back.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The series page is reused across titles; a launch that fails after the
viewer moved on is logged but not shown over the new series.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- A forced Xtream episode launch returns its promise to the menu, which
reports a rejection; the bookkeeping consumes a settled copy.
- Launch bookkeeping is guarded by a page token (series + visit), checked
again after the asynchronous save, so neither a return to the same
series nor a navigation during the save contaminates another page.
- A Stalker launch re-anchors to the host's request id after the host's
own inline teardown: only a later start supersedes it, and that still
silences the old launch's failure.
- The shared movie page disables Play and Start Over while a start is
pending, so nothing starts beside an in-flight launch.
- Episode sublines honour duration_secs before the formatted duration.
- The external session → episode ids derivation moves into the launch
helper.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- A Stalker launch that already started reports its failure even though
the host's own inline teardown retired the request id.
- An Xtream episode launch's bookkeeping applies only while the page
still shows the series it started on.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
One settle handler per owner hands the latest queued choice to the start
callback that came with it, so the page the viewer left cannot consume
the choice a reopened page made.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The choice made while a forced launch settles is kept per playlist:series
owner by the launch helper and only starts if the page still shows that
series, so a Similar-rail navigation cannot play it under another title.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- A queued Xtream episode launch checks the page owner before it closes
the running player, so a viewer who left the series keeps playback.
- A Play or episode choice made while a forced launch settles starts
once it settled instead of being dropped; only the latest choice runs.
- Both series menus disable the bulk watched actions while a start has
not published its session yet.
- The series-menu E2E helpers are shared, which brings the Stalker web
spec back under the line limit; the inline episode state derivation
moves into ui/playback.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The launch helper keeps a reactive per-series pending count; the
playback service exposes it, refuses a non-forced start while a forced
launch settles, and the series menu disables the reset row for that
window. The inline progress row moves into a shared helper.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Only a repeat of the same episode is ignored while its launch settles;
another episode of the series waits its turn and then replaces the
running player like any later start.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- A superseded Stalker launch that fails stays silent; the newer start
reports for the movie.
- Xtream episode launches are serialized per series until the launch
settles, so a repeat before the first session is published cannot start
a second player.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Provider ids are playlist-scoped: the Stalker series hero key and its
pending-start key, and the Xtream series and movie hero keys, include
the playlist so a cross-playlist navigation to the same number gets its
own layout decision and does not inherit a pending start.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- The hero keys its one-time stage/compact decision on a stable content
identity (provider + id) that every host passes, so a TMDB rename plus
a late backdrop no longer flips the visible hero.
- An explicit Stalker movie launch joins its host's start generation: it
supersedes an earlier start and is dropped once a Play, Start Over or
later launch begins, on the controller, search, collection and catalog
hosts.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The store positions are only reloaded for the playlist the reset started
on while that playlist is still current; a playlist switched meanwhile
keeps its own positions.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- The Stalker explicit-launch guard tracks every in-flight movie, so a
repeat of a pending launch is rejected even after another movie's
launch was started in between.
- The Xtream movie reset only touches the route's state while it still
shows the movie the reset started on.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A launch still resolving for the movie the user left no longer blocks
the explicit launch of the movie now on screen; only a repeat for the
same movie is ignored.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A second "Open in external player" while the first create_link is still
resolving is ignored, and the menu row is disabled for that window, so
two resolutions cannot each start a player.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The collection's selected movie id is reported only while the shown item
is a movie, so a series with the same provider number cannot let a stale
external launch start over it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- The Favorites/Recent Stalker detail wires the menu's external launch
and progress reset to collection-owned actions.
- The Stalker search host passes its pending-start state to the detail,
so the reset row is disabled while a launch resolves.
- A rejected forced Stalker episode launch is logged and reported.
- The spec mocks of the external playback handle are signals, as the
contract types them (CI spec typecheck).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The launch is awaited inside the start, so the pending state covers the
close of the previous player and the launch itself instead of clearing
as soon as the launch was kicked off.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- The external player rows of every details menu follow the managed
external player capability instead of the downloads availability.
- An explicit Stalker movie launch is a pending start for the selected
movie while its stream resolves, so the reset row is disabled meanwhile.
- The resume clock formatter moves into the presentation helpers.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A start still resolving its stream for a series the view has since left
no longer keeps the new series' reset row disabled.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- The shared movie menu offers the external player in provider-only
mode too; that mode hides local/download controls, not the provider's
stream in MPV/VLC.
- The movie and Stalker series reset-progress rows are also disabled
while a start is still resolving its stream, so a slow create_link
cannot recreate a just-cleared row.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- A forced Stalker episode launch compares against a generation captured
after the inline close instead of the one that close retired, so the
menu's MPV/VLC action starts again while stale launches still drop.
- The Stalker movie guard reports the selected id only while a movie is
on screen; a series with the same provider number never passes it.
- The series reset-progress row is disabled while an episode plays or
launches, independent of the bulk watched action's state.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Stalker movie and series menu launches in MPV/VLC close the title's
own running external session first through the new shared
replaceOwnedExternalSession(), cancelling when teardown is not
confirmed or the selection moved on.
- The reset-progress row is disabled while an external player owns the
position, on the shared and the Xtream movie menus.
- Every ISO comma-bearing country name stays whole in provider lists.
- The dashboard hero spec competes a favorite movie for a slot so the
movie-title case is actually exercised.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Closing the previous external session takes time; a launch whose series
page is gone or shows another series by then no longer starts. The
service clears its bindings on destroy and exposes the page's
playlist:series owner for that check.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- A forced MPV/VLC episode launch closes an episode of the same series
that is still running externally before starting, as the movie flow
does; a failed close keeps the running player.
- A Stalker external launch resolved after the user moved to another
movie is dropped instead of launching the old stream or closing the
new item's inline playback.
- The trailer's sound toggle is rendered by the hero above its content
layer, so it can actually be clicked.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- The movie menu's MPV/VLC launch is a regular route-owned start with
the player forced: a running external session is replaced through the
same guard instead of a second player starting beside it.
- The trailer-backdrop setting is shown for every player; it is a
details-page feature, not a web-player control.
- The series menu matches the recent list by content type as well, so a
movie sharing the provider id does not stand in for the series row,
and the hide action follows the refreshed list instead of a permanent
hidden marker.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- The "…" menu's MPV/VLC launch goes through the regular episode and
movie starts, so recent history and the launch position are recorded
as for the Play button.
- Resetting a Stalker movie's progress only clears the shown position
while that movie is still selected, and retires a position read in
flight so it cannot restore the reset row.
- The sources panel takes keyboard focus when it replaces the menu rows.
- The trailer backdrop drops its frame and waits again (muted) when the
trailer changes, and stops while the document is hidden.
- The dashboard hero strips the season marker from series only.
- Provider country lists keep "Korea, Republic of" whole.
- The web season-watched E2E reacquires the menu row after every close.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(ui): destructive confirmations, verb labels and provider icons
Confirmations: ConfirmDialogData.confirmLabel is required, so no dialog can
fall back to "Yes"/"No"; the dismiss defaults to "Cancel" and
`tone: 'destructive'` styles the confirm with .app-destructive-button. Every
caller names its action ("Remove playlist", "Clear", "Refresh playlist",
"Cancel download" with a "Close" dismiss). The confirm button has the
confirm-dialog-confirm test id and drops its no-op color="primary".
The no-op `warn` color input becomes .app-destructive-button on the EPG
mapping, playlist item, error view, EPG/reset settings, delete-all and source
cleanup buttons, and on the unsaved-changes dialog's Discard.
Provider icons come from SOURCE_TYPE_ICONS in shared/interfaces (Xtream
cloud, Stalker cast, M3U playlist_play / link / description / subject) in the
add dialog, auto-import, empty state, playlist switcher, playlist rows,
dashboard source rail, command palette, Sources filters and both reset
summaries. Stalker no longer borrows the Dashboard icon, and Xtream no longer
shares a glyph with M3U URL playlists.
The playlist error view removed a playlist through the stale
PlaylistActions.removePlaylist: it dropped the playlist from state before the
delete ran, swallowed failures, skipped the source activity guard and showed
no toast. It now uses PlaylistDeleteActionService like every other removal,
commits only a completed delete, toasts and goes home. The unused action and
its effect are removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ui): one provider icon per playlist row, imperative Korean remove label
A restored Stalker or Xtream playlist can also carry a URL, and the row's
independent checks then showed the M3U URL icon next to the provider icon.
The row now switches on resolvePlaylistSourceIconKey(), the precedence every
other surface uses, so each source shows exactly one icon.
HOME.PLAYLISTS.REMOVE now names the confirm button and the row's delete
tooltip; in Korean it read "the playlist has been removed". It now says
"remove playlist", like every other locale.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ui): keep the auto-refresh badge on playlist rows with one provider icon
Showing one provider icon per row moved the auto-refresh badge into the M3U
branches only, so a restored Stalker playlist with a URL and auto-refresh
lost it although the URL is still re-fetched. The row now renders one icon
container: the provider icon from the shared precedence, then the badge for
any row with a URL or a local M3U, exactly the rows that showed it before.
The Xtream portal-status dot, used without source health, keeps that corner.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ui): let the playlist row's cancel action render in the error color
The row's action buttons set `color: inherit`, and the selected row does so
again with more specific selectors. Both beat Material's token-driven icon
color, so the .app-destructive-button cancel action kept the row color
(selection blue on the active row). Pin the cancel button to
--mat-sys-error in both row states.
The large-deletion Electron E2E now checks the cancel color in both themes;
without this rule it reads rgb(47, 123, 255) instead of the error red.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(ui): give the dialog service spec the now-required confirm labels
ConfirmDialogData.confirmLabel became required, and the spec still built
confirmations without one. Jest only transpiles, so the suite stayed green,
but the "Typecheck Jest spec programs" CI step rejected it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>