diff --git a/libs/ui/playback/src/lib/vjs-player/vjs-player.component.spec.ts b/libs/ui/playback/src/lib/vjs-player/vjs-player.component.spec.ts index 9e48a9506..4aed167b9 100644 --- a/libs/ui/playback/src/lib/vjs-player/vjs-player.component.spec.ts +++ b/libs/ui/playback/src/lib/vjs-player/vjs-player.component.spec.ts @@ -1,4 +1,5 @@ import { SimpleChange } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; import type { VjsPlayerComponent as VjsPlayerComponentInstance } from './vjs-player.component'; const videoJsMock = jest.fn(); @@ -23,21 +24,41 @@ describe('VjsPlayerComponent', () => { let VjsPlayerComponent: typeof import('./vjs-player.component').VjsPlayerComponent; let component: VjsPlayerComponentInstance; let player: VjsPlayerComponentInstance['player']; + type SignalApiShape = { + options: () => unknown; + volume: () => number; + startTime: () => number; + timeUpdate: { emit: (event: unknown) => void }; + }; beforeAll(async () => { ({ VjsPlayerComponent } = await import('./vjs-player.component')); }); - beforeEach(() => { - component = new VjsPlayerComponent(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [VjsPlayerComponent], + }).compileComponents(); + + component = TestBed.createComponent(VjsPlayerComponent).componentInstance; player = { src: jest.fn(), reset: jest.fn(), volume: jest.fn(), + dispose: jest.fn(), } as unknown as VjsPlayerComponentInstance['player']; component.player = player; }); + it('uses signal-based inputs and outputs', () => { + const signalComponent = component as unknown as SignalApiShape; + + expect(typeof signalComponent.options).toBe('function'); + expect(signalComponent.volume()).toBe(1); + expect(signalComponent.startTime()).toBe(0); + expect(typeof signalComponent.timeUpdate.emit).toBe('function'); + }); + it('does not reset VideoJS when options change without changing the source', () => { const previousOptions = { sources: [ diff --git a/libs/ui/playback/src/lib/vjs-player/vjs-player.component.ts b/libs/ui/playback/src/lib/vjs-player/vjs-player.component.ts index 8d868aeda..86fe0bd61 100644 --- a/libs/ui/playback/src/lib/vjs-player/vjs-player.component.ts +++ b/libs/ui/playback/src/lib/vjs-player/vjs-player.component.ts @@ -1,15 +1,14 @@ import { Component, ElementRef, - EventEmitter, - Input, OnChanges, OnDestroy, OnInit, - Output, SimpleChanges, - ViewChild, ViewEncapsulation, + input, + output, + viewChild, } from '@angular/core'; import '@yangkghjh/videojs-aspect-ratio-panel'; import { getExtensionFromUrl } from 'm3u-utils'; @@ -88,16 +87,16 @@ type VideoJsPlayer = Omit< }) export class VjsPlayerComponent implements OnInit, OnChanges, OnDestroy { /** DOM-element reference */ - @ViewChild('target', { static: true }) target!: ElementRef; + readonly target = viewChild.required>('target'); /** Options of VideoJs player */ - @Input() options!: VideoPlayerOptions; + readonly options = input.required(); /** VideoJs object */ player!: VideoJsPlayer; /** mpegts.js player for raw MPEG-TS streams */ private mpegtsPlayer: mpegts.Player | null = null; - @Input() volume = 1; - @Input() startTime = 0; - @Output() timeUpdate = new EventEmitter<{ + readonly volume = input(1); + readonly startTime = input(0); + readonly timeUpdate = output<{ currentTime: number; duration: number; }>(); @@ -106,27 +105,27 @@ export class VjsPlayerComponent implements OnInit, OnChanges, OnDestroy { * Instantiate Video.js on component init */ ngOnInit(): void { - const source = this.options?.sources?.[0]; + const source = this.options().sources?.[0]; const isMpegTs = this.isMpegTsSource(source?.src); // For raw MPEG-TS streams, init Video.js without a source (UI/controls only) const vjsOptions = isMpegTs - ? { ...this.options, sources: [], autoplay: false } - : { ...this.options, autoplay: true }; + ? { ...this.options(), sources: [], autoplay: false } + : { ...this.options(), autoplay: true }; this.player = videoJs( - this.target.nativeElement, + this.target().nativeElement, vjsOptions, () => { console.log( 'Setting VideoJS player initial volume to:', - this.volume + this.volume() ); - this.player.volume(this.volume); + this.player.volume(this.volume()); this.player.on('loadedmetadata', () => { - if (this.startTime > 0) { - this.player.currentTime(this.startTime); + if (this.startTime() > 0) { + this.player.currentTime(this.startTime()); } this.logAudioTracks(); this.setupAudioTrackMenu();