test(playlist): pin the volume re-read across same-id channel entries

`createChannel` falls back to the URL for a missing id, so one stream listed
in two groups yields two entries sharing an id. The volume re-read must not
depend on ids being distinct; the linkedSignal now sources the channel itself
and the guarantee is pinned by a test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Fable 5 committed 2026-08-12 19:41:59 +02:00
1 parent 2930729e80
commit 3170b21e74
2 files changed
+27 -1

No files matched your search

@@ -357,5 +357,22 @@ describe('VideoPlayerComponent — M3U movie recognition gate', () => {
expect(component.volume()).toBe(0);
});
it('re-reads even when the next entry shares the previous id', () => {
// `createChannel` falls back to the URL for the id, so one stream
// listed under two groups yields two entries with the same id.
// Pins the user-visible guarantee independently of what the
// linkedSignal source happens to derive.
localStorage.setItem('volume', '0.8');
syncStoreState(movieChannel);
fixture.detectChanges();
expect(component.volume()).toBe(0.8);
localStorage.setItem('volume', '0.2');
syncStoreState({ ...movieChannel, group: { title: 'New' } });
fixture.detectChanges();
expect(component.volume()).toBe(0.2);
});
});
});
@@ -486,9 +486,18 @@ export class VideoPlayerComponent implements OnInit, OnDestroy {
* with as soon as the user zapped after adjusting it in the player.
* Remote-control writes still `set()` this signal directly and store the
* same value, so re-reading the bus per channel agrees with them.
*
* The source is the whole channel, not a derived key: `linkedSignal`
* re-runs its computation whenever the source EXPRESSION invalidates —
* the derived value's equality does not gate it (`producerRecomputeValue`
* calls `computation()` right after re-evaluating `source()`). Selecting
* the channel is therefore the trigger, which is exactly the intent, and
* nothing here depends on ids being distinct — `createChannel` falls back
* to the URL, so one stream listed in two groups shares an id. A re-read
* is one idempotent localStorage hit, so an extra one costs nothing.
*/
readonly volume = linkedSignal({
source: () => this.activeChannel()?.id ?? null,
source: () => this.activeChannel(),
computation: () => readStoredVolume(),
});