@memberjunction/ng-media-player 0.0.1 → 5.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +241 -28
- package/dist/lib/media-player/cue-utils.d.ts +24 -0
- package/dist/lib/media-player/cue-utils.d.ts.map +1 -0
- package/dist/lib/media-player/cue-utils.js +47 -0
- package/dist/lib/media-player/cue-utils.js.map +1 -0
- package/dist/lib/media-player/media-player.component.d.ts +326 -0
- package/dist/lib/media-player/media-player.component.d.ts.map +1 -0
- package/dist/lib/media-player/media-player.component.js +1545 -0
- package/dist/lib/media-player/media-player.component.js.map +1 -0
- package/dist/lib/media-player/playback-state.d.ts +46 -0
- package/dist/lib/media-player/playback-state.d.ts.map +1 -0
- package/dist/lib/media-player/playback-state.js +86 -0
- package/dist/lib/media-player/playback-state.js.map +1 -0
- package/dist/lib/media-player/transcript-layout.d.ts +35 -0
- package/dist/lib/media-player/transcript-layout.d.ts.map +1 -0
- package/dist/lib/media-player/transcript-layout.js +37 -0
- package/dist/lib/media-player/transcript-layout.js.map +1 -0
- package/dist/lib/media-player/waveform-utils.d.ts +45 -0
- package/dist/lib/media-player/waveform-utils.d.ts.map +1 -0
- package/dist/lib/media-player/waveform-utils.js +108 -0
- package/dist/lib/media-player/waveform-utils.js.map +1 -0
- package/dist/lib/media-player.types.d.ts +107 -0
- package/dist/lib/media-player.types.d.ts.map +1 -0
- package/dist/lib/media-player.types.js +9 -0
- package/dist/lib/media-player.types.js.map +1 -0
- package/dist/lib/storage-media-player/storage-media-player.component.d.ts +77 -0
- package/dist/lib/storage-media-player/storage-media-player.component.d.ts.map +1 -0
- package/dist/lib/storage-media-player/storage-media-player.component.js +283 -0
- package/dist/lib/storage-media-player/storage-media-player.component.js.map +1 -0
- package/dist/public-api.d.ts +8 -0
- package/dist/public-api.d.ts.map +1 -0
- package/dist/public-api.js +11 -0
- package/dist/public-api.js.map +1 -0
- package/package.json +36 -7
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-player.component.js","sourceRoot":"","sources":["../../../src/lib/media-player/media-player.component.ts","../../../src/lib/media-player/media-player.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ElementRef,\n EventEmitter,\n HostListener,\n Input,\n OnDestroy,\n Output,\n ViewChild,\n inject,\n} from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport {\n MediaCueEvent,\n MediaPlaybackState,\n MediaPlayerCancelableEvent,\n MediaRateEvent,\n MediaSeekEvent,\n MediaTrack,\n MediaTranscriptCue,\n} from '../media-player.types';\nimport { computeActiveCueIndex } from './cue-utils';\nimport { MediaStateContext, MediaStateEvent, nextPlaybackState } from './playback-state';\nimport {\n TranscriptPosition,\n resolveTranscriptToggleVisible,\n resolveTranscriptVisible,\n} from './transcript-layout';\nimport { DEFAULT_WAVEFORM_BARS, downsamplePeaks } from './waveform-utils';\n\n/**\n * Delay (ms) before the *buffering* spinner is shown, to avoid flicker on instant seeks\n * that resolve immediately. The *loading* indicator shows with no delay. If a buffering\n * state clears before this elapses, the spinner never appears.\n */\nconst BUFFERING_INDICATOR_DELAY_MS = 200;\n\n/**\n * `mj-media-player` — a generic, framework-agnostic media player.\n *\n * Renders one or many audio/video tracks with a custom transport bar (play/pause,\n * click-and-drag scrubber, skip ±N, playback-rate menu, volume + mute, fullscreen),\n * a **real** audio waveform (decoded client-side, or supplied precomputed via\n * `MediaTrack.Peaks`) that doubles as the scrubber, and an optional time-synced\n * transcript panel whose cues are clickable and auto-highlight as playback advances.\n *\n * This component has ZERO MemberJunction-core dependencies — it is pure Angular and\n * is safe to reuse in any application. The MJStorage binding lives in the separate\n * `mj-storage-media-player` wrapper.\n */\n@Component({\n selector: 'mj-media-player',\n standalone: true,\n imports: [CommonModule],\n templateUrl: './media-player.component.html',\n styleUrls: ['./media-player.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class MJMediaPlayerComponent implements OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n private host = inject(ElementRef<HTMLElement>);\n\n constructor() {\n this._prefersReducedMotion = this.detectReducedMotion();\n }\n\n // ---------------------------------------------------------------------------\n // Inputs\n // ---------------------------------------------------------------------------\n\n /** The track(s) to play. One audio track → transport bar; many videos → grid. */\n @Input()\n set Tracks(value: MediaTrack[]) {\n this._tracks = value ?? [];\n this._activeMediaEl = null;\n this.resetPlaybackState();\n this.ensureWaveforms();\n this.cdr.markForCheck();\n }\n get Tracks(): MediaTrack[] {\n return this._tracks;\n }\n private _tracks: MediaTrack[] = [];\n\n /** Optional transcript cues. When set (and `ShowTranscript`), a transcript panel renders. */\n @Input()\n set Transcript(value: MediaTranscriptCue[] | null) {\n this._transcript = value;\n this._activeCueIndex = -1;\n // A newly-supplied transcript defaults to visible (the user can hide it via the toggle).\n this._transcriptUserVisible = true;\n this.cdr.markForCheck();\n }\n get Transcript(): MediaTranscriptCue[] | null {\n return this._transcript;\n }\n private _transcript: MediaTranscriptCue[] | null = null;\n\n /** Begin playback automatically once media is ready. */\n @Input() Autoplay = false;\n /** Start playback at this position (ms) once metadata loads. */\n @Input() StartAtMs: number | null = null;\n /** Whether the transcript panel is shown (when a transcript is provided). */\n @Input() ShowTranscript = true;\n /** Whether the transcript show/hide toggle button renders in the transport bar. */\n @Input() ShowTranscriptToggle = true;\n /** Whether the playback-rate menu is shown. */\n @Input() ShowSpeedControl = true;\n /** Whether the ±skip buttons are shown. */\n @Input() ShowSkipControls = true;\n /** Whether the volume slider + mute are shown. */\n @Input() ShowVolume = true;\n /**\n * Whether the real audio waveform (which doubles as the scrubber) is shown for\n * audio-only tracks. On by default. When a track supplies {@link MediaTrack.Peaks}\n * those are rendered directly; otherwise the player decodes the track URL client-side\n * to extract real peaks, falling back to a plain progress bar if decoding fails.\n */\n @Input() ShowWaveform = true;\n /** Number of bars the waveform renders / downsamples to. */\n @Input() WaveformBarCount = DEFAULT_WAVEFORM_BARS;\n /** Whether the fullscreen button is shown for video. */\n @Input() ShowFullscreen = true;\n /** Number of seconds the skip buttons jump. */\n @Input() SkipSeconds = 30;\n /** The playback rates offered in the rate menu. */\n @Input() PlaybackRates: number[] = [0.5, 1, 1.25, 1.5, 2];\n /** The initial playback rate. */\n @Input() InitialRate = 1;\n /** The initial volume (0..1). */\n @Input() InitialVolume = 1;\n /**\n * Where the transcript panel sits relative to the media. Defaults to `'bottom'`\n * (full width below the player, filling remaining height) which reads better than\n * the narrower side layout; consumers can opt into `'side'`.\n */\n @Input() TranscriptPosition: TranscriptPosition = 'bottom';\n\n // ---------------------------------------------------------------------------\n // Outputs — Before* are cancelable (set event.Cancel = true to abort)\n // ---------------------------------------------------------------------------\n\n @Output() BeforePlay = new EventEmitter<MediaPlayerCancelableEvent>();\n @Output() AfterPlay = new EventEmitter<void>();\n @Output() BeforePause = new EventEmitter<MediaPlayerCancelableEvent>();\n @Output() AfterPause = new EventEmitter<void>();\n @Output() BeforeSeek = new EventEmitter<MediaSeekEvent>();\n @Output() AfterSeek = new EventEmitter<number>();\n @Output() BeforeRateChange = new EventEmitter<MediaRateEvent>();\n @Output() AfterRateChange = new EventEmitter<number>();\n @Output() CueActivated = new EventEmitter<MediaCueEvent>();\n @Output() CueClicked = new EventEmitter<MediaCueEvent>();\n @Output() TimeUpdate = new EventEmitter<number>();\n @Output() DurationChange = new EventEmitter<number>();\n @Output() Ended = new EventEmitter<void>();\n /** Emits whenever the primary element's {@link MediaPlaybackState} transitions. */\n @Output() StateChanged = new EventEmitter<MediaPlaybackState>();\n /** Emits the new visibility (`true` = shown) whenever the transcript is toggled. */\n @Output() TranscriptVisibilityChanged = new EventEmitter<boolean>();\n\n // ---------------------------------------------------------------------------\n // View references\n // ---------------------------------------------------------------------------\n\n /** The primary media element (the single audio/video, or the first video in a grid). */\n @ViewChild('primaryMedia') primaryMedia?: ElementRef<HTMLMediaElement>;\n\n // ---------------------------------------------------------------------------\n // Internal state\n // ---------------------------------------------------------------------------\n\n private _currentTimeMs = 0;\n private _durationMs = 0;\n private _isPlaying = false;\n private _playbackRate = 1;\n private _volume = 1;\n private _muted = false;\n private _activeCueIndex = -1;\n private _isFullscreen = false;\n private _isScrubbing = false;\n private _hasError = false;\n private _rateMenuOpen = false;\n /** Media elements we've already nudged to resolve a real (finite) duration. */\n private _durationForced = new WeakSet<HTMLMediaElement>();\n /** True while a force-duration nudge is in flight — suppresses the transient time/position jump. */\n private _suppressTimeUpdate = false;\n private _activeMediaEl: HTMLMediaElement | null = null;\n private _rateInitialized = false;\n /** Runtime (toggle-driven) transcript visibility. Reset to `true` when a transcript is set. */\n private _transcriptUserVisible = true;\n\n /** The high-level playback lifecycle state of the primary element. */\n private _mediaState: MediaPlaybackState = 'idle';\n /**\n * Whether the buffering spinner is currently visible. Distinct from `_mediaState ===\n * 'buffering'`: the state flips immediately, but the *visible* spinner is debounced by\n * {@link BUFFERING_INDICATOR_DELAY_MS} so instant seeks don't flash it.\n */\n private _bufferingVisible = false;\n /** Pending timer that promotes a `buffering` state to a visible spinner after the delay. */\n private _bufferingTimer: ReturnType<typeof setTimeout> | null = null;\n\n /** Per-track computed/supplied waveform peaks (0..1), keyed by track Id. */\n private _peaksByTrackId = new Map<string, number[]>();\n /** Track Ids whose client-side decode failed (→ fall back to a plain progress bar). */\n private _waveformFailedTrackIds = new Set<string>();\n /** Track Ids currently being decoded (avoid duplicate in-flight decodes). */\n private _waveformPendingTrackIds = new Set<string>();\n /** Lazily-created AudioContext used only for client-side peak extraction. */\n private _audioCtx: AudioContext | null = null;\n /** Whether the user prefers reduced motion (suppresses the bar draw-in animation). */\n private _prefersReducedMotion = false;\n\n // ---------------------------------------------------------------------------\n // Public getters (imperative read API + template bindings)\n // ---------------------------------------------------------------------------\n\n get CurrentTimeMs(): number {\n return this._currentTimeMs;\n }\n get DurationMs(): number {\n return this._durationMs;\n }\n get IsPlaying(): boolean {\n return this._isPlaying;\n }\n get PlaybackRate(): number {\n return this._playbackRate;\n }\n get ActiveCueIndex(): number {\n return this._activeCueIndex;\n }\n\n // Template-only convenience getters\n get Muted(): boolean {\n return this._muted;\n }\n get Volume(): number {\n return this._volume;\n }\n get IsFullscreen(): boolean {\n return this._isFullscreen;\n }\n get RateMenuOpen(): boolean {\n return this._rateMenuOpen;\n }\n get HasError(): boolean {\n return this._hasError;\n }\n\n /** The current high-level playback lifecycle state of the primary element. */\n get MediaState(): MediaPlaybackState {\n return this._mediaState;\n }\n /** True during the initial source load (shows the \"Loading…\" stage indicator). */\n get IsLoading(): boolean {\n return this._mediaState === 'loading';\n }\n /**\n * True while buffering — but reflects the *debounced* visible flag, so an instant seek\n * that resolves within {@link BUFFERING_INDICATOR_DELAY_MS} never shows the spinner.\n */\n get IsBuffering(): boolean {\n return this._bufferingVisible;\n }\n /** Whether the stage should show ANY busy indicator (loading or visible buffering). */\n get ShowBusyIndicator(): boolean {\n return this.IsLoading || this.IsBuffering;\n }\n /** The label shown alongside the busy spinner. */\n get BusyLabel(): string {\n return this.IsLoading ? 'Loading…' : 'Buffering…';\n }\n /** A short, screen-reader-friendly status phrase reflecting the current state. */\n get StatusLabel(): string {\n switch (this._mediaState) {\n case 'loading':\n return 'Loading';\n case 'buffering':\n return 'Buffering';\n case 'playing':\n return 'Playing';\n case 'paused':\n return 'Paused';\n case 'ready':\n return 'Ready';\n case 'ended':\n return 'Ended';\n case 'error':\n return 'Error';\n default:\n return 'Idle';\n }\n }\n get HasTracks(): boolean {\n return this._tracks.length > 0;\n }\n /** Whether a transcript (one or more cues) is available, regardless of visibility. */\n get HasTranscript(): boolean {\n return !!this._transcript && this._transcript.length > 0;\n }\n /**\n * Whether the transcript panel should render right now — requires a transcript,\n * the `ShowTranscript` master switch, AND the runtime toggle to be visible.\n */\n get ShowTranscriptPanel(): boolean {\n return resolveTranscriptVisible(this.HasTranscript, this.ShowTranscript, this._transcriptUserVisible);\n }\n /** The current runtime visibility of the transcript (toggle-driven). */\n get TranscriptVisible(): boolean {\n return this._transcriptUserVisible;\n }\n /** Whether the transcript show/hide toggle button should render in the transport. */\n get ShowTranscriptToggleButton(): boolean {\n return resolveTranscriptToggleVisible(this.HasTranscript, this.ShowTranscript, this.ShowTranscriptToggle);\n }\n get ScrubFraction(): number {\n return this._durationMs > 0 ? this._currentTimeMs / this._durationMs : 0;\n }\n get ScrubPercent(): number {\n return Math.min(100, Math.max(0, this.ScrubFraction * 100));\n }\n get PrefersReducedMotion(): boolean {\n return this._prefersReducedMotion;\n }\n\n /** The active audio track that drives the waveform (the first audio track, if any). */\n private get WaveformTrack(): MediaTrack | null {\n return this.IsAudioOnly ? this.AudioTracks[0] ?? null : null;\n }\n\n /**\n * True when the waveform should render as bars — i.e. it's enabled, the active track\n * is audio, and we have peaks (supplied or successfully decoded) for it. When false\n * (decode pending/failed, no track), the template shows a plain progress bar instead.\n */\n get ShowWaveformBars(): boolean {\n const track = this.WaveformTrack;\n if (!this.ShowWaveform || !track) {\n return false;\n }\n return this.peaksForTrack(track) !== null;\n }\n\n /**\n * The normalized `0..1` peaks for the active audio track, or `null` when none are\n * available yet (still decoding) or decoding failed. Reading this lazily kicks off\n * client-side extraction when needed — re-renders/seeks never re-decode (cached by Id).\n */\n get WaveformPeaks(): number[] | null {\n const track = this.WaveformTrack;\n if (!this.ShowWaveform || !track) {\n return null;\n }\n return this.peaksForTrack(track);\n }\n\n /** The played/unplayed split point for the waveform, as a `0..1` fraction. */\n get WaveformPlayedFraction(): number {\n return this.ScrubFraction;\n }\n\n /** The single audio track when this is an audio-only player (drives waveform). */\n get AudioTracks(): MediaTrack[] {\n return this._tracks.filter((t) => t.Kind === 'audio');\n }\n get VideoTracks(): MediaTrack[] {\n return this._tracks.filter((t) => t.Kind === 'video');\n }\n /** True when the layout is a single audio track with no video. */\n get IsAudioOnly(): boolean {\n return this.VideoTracks.length === 0 && this.AudioTracks.length > 0;\n }\n get IsMultiVideo(): boolean {\n return this.VideoTracks.length > 1;\n }\n\n // ---------------------------------------------------------------------------\n // Imperative control API\n // ---------------------------------------------------------------------------\n\n /** Begin playback. Honors {@link BeforePlay}. */\n Play(): void {\n const el = this.resolveMediaElement();\n if (!el) {\n return;\n }\n const evt: MediaPlayerCancelableEvent = { Cancel: false };\n this.BeforePlay.emit(evt);\n if (evt.Cancel) {\n return;\n }\n void el.play().catch(() => {\n /* autoplay/user-gesture rejections are non-fatal */\n });\n }\n\n /** Pause playback. Honors {@link BeforePause}. */\n Pause(): void {\n const el = this.resolveMediaElement();\n if (!el) {\n return;\n }\n const evt: MediaPlayerCancelableEvent = { Cancel: false };\n this.BeforePause.emit(evt);\n if (evt.Cancel) {\n return;\n }\n el.pause();\n }\n\n /** Toggle play/pause. */\n TogglePlay(): void {\n if (this._isPlaying) {\n this.Pause();\n } else {\n this.Play();\n }\n }\n\n /** Seek to an absolute position in milliseconds. Honors {@link BeforeSeek}. */\n SeekToMs(ms: number): void {\n const el = this.resolveMediaElement();\n if (!el) {\n return;\n }\n const target = this.clampMs(ms);\n const evt: MediaSeekEvent = { Cancel: false, FromMs: this._currentTimeMs, ToMs: target };\n this.BeforeSeek.emit(evt);\n if (evt.Cancel) {\n return;\n }\n el.currentTime = target / 1000;\n this._currentTimeMs = target;\n this.AfterSeek.emit(target);\n this.refreshActiveCue();\n this.cdr.markForCheck();\n }\n\n /** Skip forward by {@link SkipSeconds}. */\n SkipForward(): void {\n this.SeekToMs(this._currentTimeMs + this.SkipSeconds * 1000);\n }\n\n /** Skip backward by {@link SkipSeconds}. */\n SkipBackward(): void {\n this.SeekToMs(this._currentTimeMs - this.SkipSeconds * 1000);\n }\n\n /** Set the playback rate. Honors {@link BeforeRateChange}. */\n SetPlaybackRate(rate: number): void {\n const evt: MediaRateEvent = { Cancel: false, FromRate: this._playbackRate, ToRate: rate };\n this.BeforeRateChange.emit(evt);\n if (evt.Cancel) {\n return;\n }\n this._playbackRate = rate;\n this.applyToAllMedia((el) => (el.playbackRate = rate));\n this._rateMenuOpen = false;\n this.AfterRateChange.emit(rate);\n this.cdr.markForCheck();\n }\n\n /**\n * Seek to the cue at `index`. If currently playing, playback continues from the\n * cue's start; if paused, the timeline is repositioned without starting playback.\n */\n SeekToCue(index: number): void {\n const cues = this._transcript;\n if (!cues || index < 0 || index >= cues.length) {\n return;\n }\n const wasPlaying = this._isPlaying;\n this.SeekToMs(cues[index].StartMs);\n if (wasPlaying) {\n // keep playing — currentTime change above does not pause; ensure we're playing\n this.Play();\n }\n // when paused, we leave it paused (timeline repositioned only)\n }\n\n /** Set the volume (0..1). Unmutes if volume > 0. */\n SetVolume(v: number): void {\n this._volume = Math.min(1, Math.max(0, v));\n this._muted = this._volume === 0;\n this.applyToAllMedia((el) => {\n el.volume = this._volume;\n el.muted = this._muted;\n });\n this.cdr.markForCheck();\n }\n\n /** Toggle mute. */\n ToggleMute(): void {\n this._muted = !this._muted;\n this.applyToAllMedia((el) => (el.muted = this._muted));\n this.cdr.markForCheck();\n }\n\n /** Request fullscreen for the player surface (video). */\n EnterFullscreen(): void {\n const target = this.host.nativeElement.querySelector('.mj-media-stage') as HTMLElement | null;\n if (target?.requestFullscreen) {\n void target.requestFullscreen().catch(() => undefined);\n }\n }\n\n /** Exit fullscreen. */\n ExitFullscreen(): void {\n if (document.fullscreenElement && document.exitFullscreen) {\n void document.exitFullscreen().catch(() => undefined);\n }\n }\n\n /** Toggle fullscreen. */\n ToggleFullscreen(): void {\n if (this._isFullscreen) {\n this.ExitFullscreen();\n } else {\n this.EnterFullscreen();\n }\n }\n\n /**\n * Show/hide the transcript panel (pure component state — nothing is persisted).\n * Emits {@link TranscriptVisibilityChanged} with the new visibility.\n */\n ToggleTranscript(): void {\n this._transcriptUserVisible = !this._transcriptUserVisible;\n this.TranscriptVisibilityChanged.emit(this._transcriptUserVisible);\n this.cdr.markForCheck();\n }\n\n // ---------------------------------------------------------------------------\n // Media element event handlers (bound in template)\n // ---------------------------------------------------------------------------\n\n /** Initial source fetch began — drives the \"Loading…\" indicator. */\n OnMediaLoadStart(el: HTMLMediaElement): void {\n if (el !== this.resolveMediaElement()) {\n return;\n }\n this.applyMediaEvent('loadstart', el);\n }\n\n /** Wire-up when a media element fires its first event — caches the primary element & applies initial state. */\n OnMediaLoadedMetadata(el: HTMLMediaElement): void {\n this._hasError = false;\n if (!this._activeMediaEl) {\n this._activeMediaEl = el;\n }\n this.initializeMediaElement(el);\n const dur = el.duration;\n if (isFinite(dur) && dur > 0) {\n this._durationMs = Math.round(dur * 1000);\n this.DurationChange.emit(this._durationMs);\n } else {\n // MediaRecorder/streamed webm files often carry no duration in their header, so the\n // element reports `Infinity` — which makes fraction-based seeking compute 0 (every\n // timeline click snaps to the start). Nudge the element once to resolve a real duration;\n // this also builds its internal seek index. The resulting `durationchange` is handled in\n // OnMediaDurationChange.\n this.forceDurationResolution(el);\n }\n if (this.StartAtMs != null && el === this._activeMediaEl) {\n el.currentTime = this.clampMs(this.StartAtMs) / 1000;\n }\n if (this.Autoplay && el === this._activeMediaEl) {\n this.Play();\n }\n this.applyMediaEvent('loadedmetadata', el);\n this.cdr.markForCheck();\n }\n\n /** Enough data buffered to begin playback — clears loading/buffering. */\n OnMediaCanPlay(el: HTMLMediaElement): void {\n if (el !== this.resolveMediaElement()) {\n return;\n }\n this.applyMediaEvent('canplay', el);\n }\n\n /** Ran out of buffered data mid-playback — enters buffering. */\n OnMediaWaiting(el: HTMLMediaElement): void {\n if (el !== this.resolveMediaElement()) {\n return;\n }\n this.applyMediaEvent('waiting', el);\n }\n\n /** Seek began (skip / scrub into un-buffered territory) — show buffering during the seek. */\n OnMediaSeeking(el: HTMLMediaElement): void {\n if (el !== this.resolveMediaElement()) {\n return;\n }\n this.applyMediaEvent('seeking', el);\n }\n\n /** Seek completed — clears buffering once the element has playable data. */\n OnMediaSeeked(el: HTMLMediaElement): void {\n if (el !== this.resolveMediaElement()) {\n return;\n }\n this.applyMediaEvent('seeked', el);\n }\n\n /** Element is actively playing — clears loading + buffering. */\n OnMediaPlaying(el: HTMLMediaElement): void {\n if (el !== this.resolveMediaElement()) {\n return;\n }\n this.applyMediaEvent('playing', el);\n }\n\n /** Conservative buffering signals — only buffer when actively trying to play. */\n OnMediaStalled(el: HTMLMediaElement): void {\n if (el !== this.resolveMediaElement()) {\n return;\n }\n this.applyMediaEvent('stalled', el);\n }\n\n OnMediaSuspend(el: HTMLMediaElement): void {\n if (el !== this.resolveMediaElement()) {\n return;\n }\n this.applyMediaEvent('suspend', el);\n }\n\n /** Track switch / source cleared — resets the lifecycle. */\n OnMediaEmptied(el: HTMLMediaElement): void {\n if (el !== this.resolveMediaElement()) {\n return;\n }\n this.applyMediaEvent('emptied', el);\n }\n\n OnMediaTimeUpdate(el: HTMLMediaElement): void {\n if (el !== this.resolveMediaElement()) {\n return;\n }\n if (this._suppressTimeUpdate) {\n // Ignore the transient end-seek the duration nudge produces.\n return;\n }\n this._currentTimeMs = Math.round(el.currentTime * 1000);\n this.TimeUpdate.emit(this._currentTimeMs);\n this.refreshActiveCue();\n this.cdr.markForCheck();\n }\n\n OnMediaDurationChange(el: HTMLMediaElement): void {\n if (el !== this.resolveMediaElement()) {\n return;\n }\n if (isFinite(el.duration)) {\n this._durationMs = Math.round(el.duration * 1000);\n this.DurationChange.emit(this._durationMs);\n this.cdr.markForCheck();\n }\n }\n\n /**\n * Forces a header-less media element (e.g. a MediaRecorder webm that reports `Infinity`\n * duration) to resolve a real, finite duration by briefly seeking far past the end. The browser\n * clamps to the true end, fires `durationchange` with the real value (picked up by\n * {@link OnMediaDurationChange}) and builds its seek index — after which fraction-based seeking\n * works. We then restore the prior position and suppress the transient time jump. Runs at most\n * once per element.\n */\n private forceDurationResolution(el: HTMLMediaElement): void {\n if (this._durationForced.has(el)) {\n return;\n }\n this._durationForced.add(el);\n const restoreSeconds = el.currentTime || 0;\n this._suppressTimeUpdate = true;\n const onResolved = (): void => {\n if (!isFinite(el.duration)) {\n return; // not the real value yet — wait for the next durationchange\n }\n el.removeEventListener('durationchange', onResolved);\n this._suppressTimeUpdate = false;\n try {\n el.currentTime = restoreSeconds;\n } catch {\n /* best effort — restoring position is non-critical */\n }\n };\n el.addEventListener('durationchange', onResolved);\n try {\n el.currentTime = 1e101;\n } catch {\n el.removeEventListener('durationchange', onResolved);\n this._suppressTimeUpdate = false;\n }\n }\n\n OnMediaPlay(el: HTMLMediaElement): void {\n if (el !== this.resolveMediaElement()) {\n return;\n }\n this._isPlaying = true;\n this.applyMediaEvent('play', el);\n this.AfterPlay.emit();\n this.cdr.markForCheck();\n }\n\n OnMediaPause(el: HTMLMediaElement): void {\n if (el !== this.resolveMediaElement()) {\n return;\n }\n this._isPlaying = false;\n this.applyMediaEvent('pause', el);\n this.AfterPause.emit();\n this.cdr.markForCheck();\n }\n\n OnMediaEnded(el: HTMLMediaElement): void {\n if (el !== this.resolveMediaElement()) {\n return;\n }\n this._isPlaying = false;\n this.applyMediaEvent('ended', el);\n this.Ended.emit();\n this.cdr.markForCheck();\n }\n\n OnMediaError(el?: HTMLMediaElement): void {\n this._hasError = true;\n this.applyMediaEvent('error', el ?? this.resolveMediaElement());\n this.cdr.markForCheck();\n }\n\n // ---------------------------------------------------------------------------\n // Playback state machine (driven by the pure reducer + buffering debounce)\n // ---------------------------------------------------------------------------\n\n /**\n * Runs the pure {@link nextPlaybackState} reducer for a native media event, commits the\n * resulting state, manages the debounced buffering spinner, and emits {@link StateChanged}\n * on transitions. The single funnel for all media-event → state changes.\n */\n private applyMediaEvent(event: MediaStateEvent, el: HTMLMediaElement | null): void {\n const ctx: MediaStateContext = {\n IsPlaying: this._isPlaying,\n ReadyState: el?.readyState ?? 0,\n Ended: el?.ended ?? false,\n };\n const next = nextPlaybackState(event, this._mediaState, ctx);\n this.setMediaState(next);\n }\n\n /** Commits a new state, syncs the buffering spinner, and emits on change. */\n private setMediaState(next: MediaPlaybackState): void {\n this.syncBufferingIndicator(next);\n if (next !== this._mediaState) {\n this._mediaState = next;\n this.StateChanged.emit(next);\n this.cdr.markForCheck();\n }\n }\n\n /**\n * Keeps the visible buffering spinner in sync with the target state. `loading` shows\n * immediately (no debounce); `buffering` is debounced so instant seeks don't flash it;\n * any non-busy state clears the spinner and cancels a pending show.\n */\n private syncBufferingIndicator(next: MediaPlaybackState): void {\n if (next === 'buffering') {\n if (this._bufferingVisible || this._bufferingTimer) {\n return; // already shown or scheduled\n }\n this._bufferingTimer = setTimeout(() => {\n this._bufferingTimer = null;\n if (this._mediaState === 'buffering') {\n this._bufferingVisible = true;\n this.cdr.markForCheck();\n }\n }, BUFFERING_INDICATOR_DELAY_MS);\n return;\n }\n // Any non-buffering target clears the pending/visible buffering spinner.\n this.clearBufferingTimer();\n if (this._bufferingVisible) {\n this._bufferingVisible = false;\n }\n }\n\n private clearBufferingTimer(): void {\n if (this._bufferingTimer) {\n clearTimeout(this._bufferingTimer);\n this._bufferingTimer = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Scrubber (click + drag) — pointer-based\n // ---------------------------------------------------------------------------\n\n OnScrubPointerDown(event: PointerEvent, trackEl: HTMLElement): void {\n event.preventDefault();\n this._isScrubbing = true;\n (event.target as Element).setPointerCapture?.(event.pointerId);\n this.applyScrubFromPointer(event, trackEl);\n }\n\n OnScrubPointerMove(event: PointerEvent, trackEl: HTMLElement): void {\n if (!this._isScrubbing) {\n return;\n }\n this.applyScrubFromPointer(event, trackEl);\n }\n\n OnScrubPointerUp(event: PointerEvent): void {\n if (!this._isScrubbing) {\n return;\n }\n this._isScrubbing = false;\n (event.target as Element).releasePointerCapture?.(event.pointerId);\n }\n\n // The waveform IS a scrubber: reuse the exact same pointer/keyboard seek logic.\n OnWaveformPointerDown(event: PointerEvent, trackEl: HTMLElement): void {\n this.OnScrubPointerDown(event, trackEl);\n }\n OnWaveformPointerMove(event: PointerEvent, trackEl: HTMLElement): void {\n this.OnScrubPointerMove(event, trackEl);\n }\n OnWaveformPointerUp(event: PointerEvent): void {\n this.OnScrubPointerUp(event);\n }\n\n /** Keyboard support for the scrubber (role=\"slider\"). */\n OnScrubKeyDown(event: KeyboardEvent): void {\n let handled = true;\n switch (event.key) {\n case 'ArrowLeft':\n this.SeekToMs(this._currentTimeMs - 5000);\n break;\n case 'ArrowRight':\n this.SeekToMs(this._currentTimeMs + 5000);\n break;\n case 'Home':\n this.SeekToMs(0);\n break;\n case 'End':\n this.SeekToMs(this._durationMs);\n break;\n default:\n handled = false;\n }\n if (handled) {\n event.preventDefault();\n }\n }\n\n // ---------------------------------------------------------------------------\n // Transcript cue interaction\n // ---------------------------------------------------------------------------\n\n OnCueClicked(index: number): void {\n const cues = this._transcript;\n if (!cues || index < 0 || index >= cues.length) {\n return;\n }\n this.CueClicked.emit({ Cue: cues[index], Index: index });\n this.SeekToCue(index);\n }\n\n OnCueKeyDown(event: KeyboardEvent, index: number): void {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n this.OnCueClicked(index);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Rate menu\n // ---------------------------------------------------------------------------\n\n ToggleRateMenu(): void {\n this._rateMenuOpen = !this._rateMenuOpen;\n this.cdr.markForCheck();\n }\n\n // ---------------------------------------------------------------------------\n // Volume slider input\n // ---------------------------------------------------------------------------\n\n OnVolumeInput(event: Event): void {\n const value = parseFloat((event.target as HTMLInputElement).value);\n this.SetVolume(value);\n }\n\n // ---------------------------------------------------------------------------\n // Keyboard shortcuts (host-level, when the player has focus)\n // ---------------------------------------------------------------------------\n\n @HostListener('keydown', ['$event'])\n OnHostKeyDown(event: KeyboardEvent): void {\n // ignore typing in form controls (e.g. the volume range / scrubber handle has its own handler)\n const target = event.target as HTMLElement;\n if (target?.tagName === 'INPUT' || target?.getAttribute('role') === 'slider') {\n return;\n }\n switch (event.key) {\n case ' ':\n event.preventDefault();\n this.TogglePlay();\n break;\n case 'ArrowLeft':\n event.preventDefault();\n this.SeekToMs(this._currentTimeMs - 5000);\n break;\n case 'ArrowRight':\n event.preventDefault();\n this.SeekToMs(this._currentTimeMs + 5000);\n break;\n case 'j':\n case 'J':\n event.preventDefault();\n this.SeekToMs(this._currentTimeMs - 10000);\n break;\n case 'l':\n case 'L':\n event.preventDefault();\n this.SeekToMs(this._currentTimeMs + 10000);\n break;\n case 'ArrowUp':\n event.preventDefault();\n this.SetVolume(this._volume + 0.05);\n break;\n case 'ArrowDown':\n event.preventDefault();\n this.SetVolume(this._volume - 0.05);\n break;\n case 'm':\n case 'M':\n event.preventDefault();\n this.ToggleMute();\n break;\n case 'f':\n case 'F':\n if (this.ShowFullscreen && this.VideoTracks.length > 0) {\n event.preventDefault();\n this.ToggleFullscreen();\n }\n break;\n default:\n break;\n }\n }\n\n @HostListener('document:fullscreenchange')\n OnFullscreenChange(): void {\n this._isFullscreen = !!document.fullscreenElement;\n this.cdr.markForCheck();\n }\n\n // ---------------------------------------------------------------------------\n // Formatting helpers (template)\n // ---------------------------------------------------------------------------\n\n /** Formats a millisecond value as `mm:ss` (or `h:mm:ss` for long media). */\n FormatTime(ms: number): string {\n if (!isFinite(ms) || ms < 0) {\n ms = 0;\n }\n const totalSeconds = Math.floor(ms / 1000);\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n const mm = hours > 0 ? String(minutes).padStart(2, '0') : String(minutes);\n const ss = String(seconds).padStart(2, '0');\n return hours > 0 ? `${hours}:${mm}:${ss}` : `${mm}:${ss}`;\n }\n\n /** Stable color for a speaker label (deterministic hue from the label string). */\n SpeakerColor(label: string | undefined): string {\n if (!label) {\n return 'var(--mj-text-secondary)';\n }\n let hash = 0;\n for (let i = 0; i < label.length; i++) {\n hash = (hash * 31 + label.charCodeAt(i)) & 0xffffffff;\n }\n const hue = Math.abs(hash) % 360;\n return `hsl(${hue}, 55%, 45%)`;\n }\n\n TrackById(_index: number, track: MediaTrack): string {\n return track.Id;\n }\n\n CueTrackById(_index: number, cue: MediaTranscriptCue): string {\n return cue.Id;\n }\n\n // ---------------------------------------------------------------------------\n // Internal helpers\n // ---------------------------------------------------------------------------\n\n /**\n * The element that owns the master timeline. In a single audio track or a single\n * video, that's the primary element. For one-video-plus-audio, the video carries\n * playback (audio track is muted/secondary). For multi-video, the first video leads.\n */\n private resolveMediaElement(): HTMLMediaElement | null {\n if (this._activeMediaEl) {\n return this._activeMediaEl;\n }\n return this.primaryMedia?.nativeElement ?? null;\n }\n\n private initializeMediaElement(el: HTMLMediaElement): void {\n if (!this._rateInitialized) {\n this._playbackRate = this.InitialRate;\n this._volume = this.InitialVolume;\n this._rateInitialized = true;\n }\n el.playbackRate = this._playbackRate;\n el.volume = this._volume;\n el.muted = this._muted;\n }\n\n private applyToAllMedia(fn: (el: HTMLMediaElement) => void): void {\n const els = this.host.nativeElement.querySelectorAll('audio, video');\n els.forEach((el: Element) => fn(el as HTMLMediaElement));\n }\n\n private applyScrubFromPointer(event: PointerEvent, trackEl: HTMLElement): void {\n const rect = trackEl.getBoundingClientRect();\n if (rect.width <= 0) {\n return;\n }\n const fraction = Math.min(1, Math.max(0, (event.clientX - rect.left) / rect.width));\n this.SeekToMs(fraction * this._durationMs);\n }\n\n private refreshActiveCue(): void {\n const newIndex = computeActiveCueIndex(this._currentTimeMs, this._transcript);\n if (newIndex !== this._activeCueIndex) {\n this._activeCueIndex = newIndex;\n if (newIndex >= 0 && this._transcript) {\n this.CueActivated.emit({ Cue: this._transcript[newIndex], Index: newIndex });\n }\n }\n }\n\n private clampMs(ms: number): number {\n if (ms < 0) {\n return 0;\n }\n if (this._durationMs > 0 && ms > this._durationMs) {\n return this._durationMs;\n }\n return ms;\n }\n\n private resetPlaybackState(): void {\n this._currentTimeMs = 0;\n this._durationMs = 0;\n this._isPlaying = false;\n this._activeCueIndex = -1;\n this._hasError = false;\n this._rateInitialized = false;\n this.clearBufferingTimer();\n this._bufferingVisible = false;\n this.setMediaState(this._tracks.length > 0 ? 'loading' : 'idle');\n }\n\n // ---------------------------------------------------------------------------\n // Waveform peak extraction (client-side, best-effort, cached per track Id)\n // ---------------------------------------------------------------------------\n\n /**\n * Resolves peaks for a track: supplied {@link MediaTrack.Peaks} win (rendered as-is,\n * no decode); otherwise returns any cached/decoded peaks, kicking off a best-effort\n * client-side decode on first miss. Returns `null` while decoding or after a failure.\n */\n private peaksForTrack(track: MediaTrack): number[] | null {\n if (track.Peaks && track.Peaks.length > 0) {\n return track.Peaks;\n }\n const cached = this._peaksByTrackId.get(track.Id);\n if (cached) {\n return cached;\n }\n if (this._waveformFailedTrackIds.has(track.Id)) {\n return null;\n }\n void this.extractPeaks(track);\n return null;\n }\n\n /** Kicks off client-side extraction for every audio track that still needs it. */\n private ensureWaveforms(): void {\n if (!this.ShowWaveform) {\n return;\n }\n const track = this.IsAudioOnly ? this.AudioTracks[0] : null;\n if (track) {\n // Reading via peaksForTrack triggers a decode when needed (and is cache-safe).\n this.peaksForTrack(track);\n }\n }\n\n /**\n * Best-effort: fetch → arrayBuffer → decodeAudioData → downsample channel 0 → cache.\n * Never throws into the view; on any failure marks the track as failed so the template\n * falls back to a plain progress bar (not the old synthetic bars).\n */\n private async extractPeaks(track: MediaTrack): Promise<void> {\n if (this._waveformPendingTrackIds.has(track.Id)) {\n return;\n }\n this._waveformPendingTrackIds.add(track.Id);\n try {\n const ctx = this.resolveAudioContext();\n if (!ctx) {\n this.markWaveformFailed(track.Id);\n return;\n }\n const response = await fetch(track.Url);\n if (!response.ok) {\n this.markWaveformFailed(track.Id);\n return;\n }\n const arrayBuffer = await response.arrayBuffer();\n const audioBuffer = await ctx.decodeAudioData(arrayBuffer.slice(0));\n const channel = audioBuffer.getChannelData(0);\n const peaks = downsamplePeaks(channel, this.WaveformBarCount, 'max-abs');\n this._peaksByTrackId.set(track.Id, peaks);\n this.cdr.markForCheck();\n } catch {\n // CORS, unsupported codec, decode error → graceful fallback (no throw).\n this.markWaveformFailed(track.Id);\n } finally {\n this._waveformPendingTrackIds.delete(track.Id);\n }\n }\n\n private markWaveformFailed(trackId: string): void {\n this._waveformFailedTrackIds.add(trackId);\n this.cdr.markForCheck();\n }\n\n /** Lazily creates a single AudioContext used only for offline peak extraction. */\n private resolveAudioContext(): AudioContext | null {\n if (this._audioCtx) {\n return this._audioCtx;\n }\n const Ctor =\n typeof window !== 'undefined'\n ? window.AudioContext ?? (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext\n : undefined;\n if (!Ctor) {\n return null;\n }\n try {\n this._audioCtx = new Ctor();\n return this._audioCtx;\n } catch {\n return null;\n }\n }\n\n private detectReducedMotion(): boolean {\n try {\n return typeof window !== 'undefined' && !!window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;\n } catch {\n return false;\n }\n }\n\n ngOnDestroy(): void {\n this.clearBufferingTimer();\n // object URLs are owned by the wrapper that created them; close our decode context.\n if (this._audioCtx) {\n void this._audioCtx.close().catch(() => undefined);\n this._audioCtx = null;\n }\n }\n}\n","<div\n class=\"mj-media-player\"\n [class.mj-media-player--bottom-transcript]=\"TranscriptPosition === 'bottom'\"\n [class.mj-media-player--has-transcript]=\"ShowTranscriptPanel\"\n [class.mj-media-player--audio]=\"IsAudioOnly\"\n [class.mj-media-player--video]=\"VideoTracks.length > 0\"\n tabindex=\"0\"\n role=\"group\"\n aria-label=\"Media player\"\n>\n @if (!HasTracks) {\n <!-- Empty / no-track state -->\n <div class=\"mj-media-empty\" role=\"status\">\n <i class=\"fa-solid fa-circle-play mj-media-empty__icon\" aria-hidden=\"true\"></i>\n <span class=\"mj-media-empty__text\">No media to play.</span>\n </div>\n } @else {\n <div class=\"mj-media-main\">\n <!-- Media stage -->\n <div class=\"mj-media-stage\">\n @if (HasError) {\n <div class=\"mj-media-error\" role=\"alert\">\n <i class=\"fa-solid fa-triangle-exclamation\" aria-hidden=\"true\"></i>\n <span>This media could not be played.</span>\n </div>\n }\n\n <!-- Loading / buffering overlay (primary element only). -->\n @if (ShowBusyIndicator && !HasError) {\n <div\n class=\"mj-media-busy\"\n [class.mj-media-busy--static]=\"PrefersReducedMotion\"\n role=\"status\"\n aria-hidden=\"true\"\n >\n <span class=\"mj-media-busy__spinner\"></span>\n <span class=\"mj-media-busy__label\">{{ BusyLabel }}</span>\n </div>\n }\n\n @if (IsAudioOnly) {\n <!-- Single audio track: hidden <audio> + optional waveform -->\n @for (track of AudioTracks; track TrackById($index, track); let first = $first) {\n @if (first) {\n <audio\n #primaryMedia\n class=\"mj-media-audio\"\n [src]=\"track.Url\"\n preload=\"metadata\"\n (loadstart)=\"OnMediaLoadStart($any($event.target))\"\n (loadedmetadata)=\"OnMediaLoadedMetadata($any($event.target))\"\n (canplay)=\"OnMediaCanPlay($any($event.target))\"\n (canplaythrough)=\"OnMediaCanPlay($any($event.target))\"\n (waiting)=\"OnMediaWaiting($any($event.target))\"\n (seeking)=\"OnMediaSeeking($any($event.target))\"\n (seeked)=\"OnMediaSeeked($any($event.target))\"\n (playing)=\"OnMediaPlaying($any($event.target))\"\n (stalled)=\"OnMediaStalled($any($event.target))\"\n (suspend)=\"OnMediaSuspend($any($event.target))\"\n (emptied)=\"OnMediaEmptied($any($event.target))\"\n (timeupdate)=\"OnMediaTimeUpdate($any($event.target))\"\n (durationchange)=\"OnMediaDurationChange($any($event.target))\"\n (play)=\"OnMediaPlay($any($event.target))\"\n (pause)=\"OnMediaPause($any($event.target))\"\n (ended)=\"OnMediaEnded($any($event.target))\"\n (error)=\"OnMediaError($any($event.target))\"\n ></audio>\n }\n }\n @if (ShowWaveform && ShowWaveformBars) {\n <!-- Real audio waveform — doubles as the scrubber (click/drag to seek). -->\n <div\n #waveTrack\n class=\"mj-media-waveform\"\n [class.mj-media-waveform--static]=\"PrefersReducedMotion\"\n role=\"slider\"\n tabindex=\"0\"\n aria-label=\"Seek\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"DurationMs\"\n [attr.aria-valuenow]=\"CurrentTimeMs\"\n [attr.aria-valuetext]=\"FormatTime(CurrentTimeMs) + ' of ' + FormatTime(DurationMs)\"\n (pointerdown)=\"OnWaveformPointerDown($event, waveTrack)\"\n (pointermove)=\"OnWaveformPointerMove($event, waveTrack)\"\n (pointerup)=\"OnWaveformPointerUp($event)\"\n (pointercancel)=\"OnWaveformPointerUp($event)\"\n (keydown)=\"OnScrubKeyDown($event)\"\n >\n @for (p of WaveformPeaks; track $index; let i = $index; let count = $count) {\n <span\n class=\"mj-media-waveform__bar\"\n [class.mj-media-waveform__bar--played]=\"(i + 1) / count <= WaveformPlayedFraction\"\n [style.height.%]=\"2 + p * 98\"\n ></span>\n }\n <div class=\"mj-media-waveform__progress\" [style.left.%]=\"ScrubPercent\"></div>\n </div>\n } @else if (ShowWaveform) {\n <!-- Decode pending or failed (CORS/codec/etc) → plain progress bar fallback. -->\n <div\n #waveTrack\n class=\"mj-media-waveform-fallback\"\n role=\"slider\"\n tabindex=\"0\"\n aria-label=\"Seek\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"DurationMs\"\n [attr.aria-valuenow]=\"CurrentTimeMs\"\n [attr.aria-valuetext]=\"FormatTime(CurrentTimeMs) + ' of ' + FormatTime(DurationMs)\"\n (pointerdown)=\"OnWaveformPointerDown($event, waveTrack)\"\n (pointermove)=\"OnWaveformPointerMove($event, waveTrack)\"\n (pointerup)=\"OnWaveformPointerUp($event)\"\n (pointercancel)=\"OnWaveformPointerUp($event)\"\n (keydown)=\"OnScrubKeyDown($event)\"\n >\n <div class=\"mj-media-waveform-fallback__rail\">\n <div class=\"mj-media-waveform-fallback__fill\" [style.width.%]=\"ScrubPercent\"></div>\n </div>\n </div>\n } @else {\n <div class=\"mj-media-audio-art\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-music\"></i>\n </div>\n }\n } @else {\n <!-- Video: single, or responsive grid for multiple -->\n <div\n class=\"mj-media-video-grid\"\n [class.mj-media-video-grid--multi]=\"IsMultiVideo\"\n >\n @for (track of VideoTracks; track TrackById($index, track); let i = $index) {\n <div class=\"mj-media-video-cell\">\n @if (track.Label) {\n <span class=\"mj-media-video-cell__label\">{{ track.Label }}</span>\n }\n @if (i === 0) {\n <video\n #primaryMedia\n class=\"mj-media-video\"\n [src]=\"track.Url\"\n [poster]=\"track.PosterUrl || null\"\n playsinline\n preload=\"metadata\"\n (loadstart)=\"OnMediaLoadStart($any($event.target))\"\n (loadedmetadata)=\"OnMediaLoadedMetadata($any($event.target))\"\n (canplay)=\"OnMediaCanPlay($any($event.target))\"\n (canplaythrough)=\"OnMediaCanPlay($any($event.target))\"\n (waiting)=\"OnMediaWaiting($any($event.target))\"\n (seeking)=\"OnMediaSeeking($any($event.target))\"\n (seeked)=\"OnMediaSeeked($any($event.target))\"\n (playing)=\"OnMediaPlaying($any($event.target))\"\n (stalled)=\"OnMediaStalled($any($event.target))\"\n (suspend)=\"OnMediaSuspend($any($event.target))\"\n (emptied)=\"OnMediaEmptied($any($event.target))\"\n (timeupdate)=\"OnMediaTimeUpdate($any($event.target))\"\n (durationchange)=\"OnMediaDurationChange($any($event.target))\"\n (play)=\"OnMediaPlay($any($event.target))\"\n (pause)=\"OnMediaPause($any($event.target))\"\n (ended)=\"OnMediaEnded($any($event.target))\"\n (error)=\"OnMediaError($any($event.target))\"\n ></video>\n } @else {\n <!-- Secondary videos follow the leader's timeline; no own controls -->\n <video\n class=\"mj-media-video\"\n [src]=\"track.Url\"\n [poster]=\"track.PosterUrl || null\"\n playsinline\n preload=\"metadata\"\n muted\n ></video>\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Transport bar -->\n <div class=\"mj-media-transport\">\n <!-- Screen-reader + visible playback status (announced on transitions). -->\n <div\n class=\"mj-media-status\"\n [class.mj-media-status--busy]=\"ShowBusyIndicator\"\n role=\"status\"\n aria-live=\"polite\"\n >\n @if (ShowBusyIndicator) {\n <span class=\"mj-media-status__dot\" aria-hidden=\"true\"></span>\n }\n <span class=\"mj-media-status__text\">{{ StatusLabel }}</span>\n </div>\n\n <!-- Scrubber -->\n <div class=\"mj-media-scrubber-row\">\n <span class=\"mj-media-time\">{{ FormatTime(CurrentTimeMs) }}</span>\n <div\n #scrubTrack\n class=\"mj-media-scrubber\"\n role=\"slider\"\n tabindex=\"0\"\n aria-label=\"Seek\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"DurationMs\"\n [attr.aria-valuenow]=\"CurrentTimeMs\"\n [attr.aria-valuetext]=\"FormatTime(CurrentTimeMs) + ' of ' + FormatTime(DurationMs)\"\n (pointerdown)=\"OnScrubPointerDown($event, scrubTrack)\"\n (pointermove)=\"OnScrubPointerMove($event, scrubTrack)\"\n (pointerup)=\"OnScrubPointerUp($event)\"\n (pointercancel)=\"OnScrubPointerUp($event)\"\n (keydown)=\"OnScrubKeyDown($event)\"\n >\n <div class=\"mj-media-scrubber__rail\">\n <div class=\"mj-media-scrubber__fill\" [style.width.%]=\"ScrubPercent\"></div>\n </div>\n <div class=\"mj-media-scrubber__handle\" [style.left.%]=\"ScrubPercent\"></div>\n </div>\n <span class=\"mj-media-time mj-media-time--total\">{{ FormatTime(DurationMs) }}</span>\n </div>\n\n <!-- Controls -->\n <div class=\"mj-media-controls\">\n <div class=\"mj-media-controls__left\">\n @if (ShowSkipControls) {\n <button\n type=\"button\"\n class=\"mj-media-btn\"\n [attr.aria-label]=\"'Skip back ' + SkipSeconds + ' seconds'\"\n title=\"Skip back {{ SkipSeconds }}s\"\n (click)=\"SkipBackward()\"\n >\n <i class=\"fa-solid fa-rotate-left\" aria-hidden=\"true\"></i>\n <span class=\"mj-media-btn__skip-label\">{{ SkipSeconds }}</span>\n </button>\n }\n\n <button\n type=\"button\"\n class=\"mj-media-btn mj-media-btn--primary\"\n [attr.aria-label]=\"IsPlaying ? 'Pause' : 'Play'\"\n [title]=\"IsPlaying ? 'Pause' : 'Play'\"\n (click)=\"TogglePlay()\"\n >\n <i class=\"fa-solid\" [class.fa-pause]=\"IsPlaying\" [class.fa-play]=\"!IsPlaying\" aria-hidden=\"true\"></i>\n </button>\n\n @if (ShowSkipControls) {\n <button\n type=\"button\"\n class=\"mj-media-btn\"\n [attr.aria-label]=\"'Skip forward ' + SkipSeconds + ' seconds'\"\n title=\"Skip forward {{ SkipSeconds }}s\"\n (click)=\"SkipForward()\"\n >\n <i class=\"fa-solid fa-rotate-right\" aria-hidden=\"true\"></i>\n <span class=\"mj-media-btn__skip-label\">{{ SkipSeconds }}</span>\n </button>\n }\n </div>\n\n <div class=\"mj-media-controls__right\">\n @if (ShowVolume) {\n <div class=\"mj-media-volume\">\n <button\n type=\"button\"\n class=\"mj-media-btn\"\n [attr.aria-label]=\"Muted ? 'Unmute' : 'Mute'\"\n [title]=\"Muted ? 'Unmute' : 'Mute'\"\n (click)=\"ToggleMute()\"\n >\n <i\n class=\"fa-solid\"\n [class.fa-volume-xmark]=\"Muted || Volume === 0\"\n [class.fa-volume-low]=\"!Muted && Volume > 0 && Volume <= 0.5\"\n [class.fa-volume-high]=\"!Muted && Volume > 0.5\"\n aria-hidden=\"true\"\n ></i>\n </button>\n <input\n type=\"range\"\n class=\"mj-media-volume__slider\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n [value]=\"Muted ? 0 : Volume\"\n aria-label=\"Volume\"\n (input)=\"OnVolumeInput($event)\"\n />\n </div>\n }\n\n @if (ShowSpeedControl) {\n <div class=\"mj-media-rate\">\n <button\n type=\"button\"\n class=\"mj-media-btn mj-media-rate__toggle\"\n aria-haspopup=\"true\"\n [attr.aria-expanded]=\"RateMenuOpen\"\n aria-label=\"Playback speed\"\n title=\"Playback speed\"\n (click)=\"ToggleRateMenu()\"\n >\n {{ PlaybackRate }}×\n </button>\n @if (RateMenuOpen) {\n <ul class=\"mj-media-rate__menu\" role=\"menu\">\n @for (rate of PlaybackRates; track rate) {\n <li role=\"none\">\n <button\n type=\"button\"\n role=\"menuitemradio\"\n [attr.aria-checked]=\"rate === PlaybackRate\"\n class=\"mj-media-rate__item\"\n [class.mj-media-rate__item--active]=\"rate === PlaybackRate\"\n (click)=\"SetPlaybackRate(rate)\"\n >\n {{ rate }}×\n </button>\n </li>\n }\n </ul>\n }\n </div>\n }\n\n @if (ShowTranscriptToggleButton) {\n <button\n type=\"button\"\n class=\"mj-media-btn\"\n [class.mj-media-btn--active]=\"TranscriptVisible\"\n [attr.aria-pressed]=\"TranscriptVisible\"\n [attr.aria-label]=\"TranscriptVisible ? 'Hide transcript' : 'Show transcript'\"\n [title]=\"TranscriptVisible ? 'Hide transcript' : 'Show transcript'\"\n (click)=\"ToggleTranscript()\"\n >\n <i class=\"fa-solid fa-closed-captioning\" aria-hidden=\"true\"></i>\n </button>\n }\n\n @if (ShowFullscreen && VideoTracks.length > 0) {\n <button\n type=\"button\"\n class=\"mj-media-btn\"\n [attr.aria-label]=\"IsFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'\"\n [title]=\"IsFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'\"\n (click)=\"ToggleFullscreen()\"\n >\n <i\n class=\"fa-solid\"\n [class.fa-compress]=\"IsFullscreen\"\n [class.fa-expand]=\"!IsFullscreen\"\n aria-hidden=\"true\"\n ></i>\n </button>\n }\n </div>\n </div>\n </div>\n </div>\n\n <!-- Transcript panel -->\n @if (ShowTranscriptPanel) {\n <div class=\"mj-media-transcript\" role=\"list\" aria-label=\"Transcript\">\n @for (cue of Transcript; track CueTrackById($index, cue); let i = $index) {\n <button\n type=\"button\"\n role=\"listitem\"\n class=\"mj-media-cue\"\n [class.mj-media-cue--active]=\"i === ActiveCueIndex\"\n [attr.aria-current]=\"i === ActiveCueIndex ? 'true' : null\"\n (click)=\"OnCueClicked(i)\"\n (keydown)=\"OnCueKeyDown($event, i)\"\n >\n <span class=\"mj-media-cue__time\">{{ FormatTime(cue.StartMs) }}</span>\n <span class=\"mj-media-cue__body\">\n @if (cue.SpeakerLabel) {\n <span class=\"mj-media-cue__speaker\" [style.color]=\"SpeakerColor(cue.SpeakerLabel)\">\n {{ cue.SpeakerLabel }}\n </span>\n }\n <span class=\"mj-media-cue__text\">{{ cue.Text }}</span>\n </span>\n </button>\n }\n </div>\n }\n }\n</div>\n"]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { MediaPlaybackState } from '../media-player.types';
|
|
2
|
+
/**
|
|
3
|
+
* The native HTMLMediaElement event names that drive the {@link MediaPlaybackState}
|
|
4
|
+
* machine. We bind exactly these on the PRIMARY media element.
|
|
5
|
+
*/
|
|
6
|
+
export type MediaStateEvent = 'loadstart' | 'loadedmetadata' | 'canplay' | 'canplaythrough' | 'waiting' | 'seeking' | 'seeked' | 'playing' | 'play' | 'pause' | 'ended' | 'error' | 'stalled' | 'suspend' | 'emptied';
|
|
7
|
+
/**
|
|
8
|
+
* Context the reducer needs beyond the raw event + current state. These are read off the
|
|
9
|
+
* media element at the moment the event fires (kept as plain inputs so the reducer stays
|
|
10
|
+
* a pure, fully unit-testable function with no DOM dependency).
|
|
11
|
+
*/
|
|
12
|
+
export interface MediaStateContext {
|
|
13
|
+
/** Whether the user intends playback (the element is not paused). Drives ready-vs-playing
|
|
14
|
+
* and whether conservative events (`stalled`/`suspend`) should show buffering. */
|
|
15
|
+
IsPlaying: boolean;
|
|
16
|
+
/** The element's `readyState` (HAVE_NOTHING=0 … HAVE_ENOUGH_DATA=4). Used by `seeked` to
|
|
17
|
+
* decide whether we can clear buffering immediately or must wait for `canplay`. */
|
|
18
|
+
ReadyState: number;
|
|
19
|
+
/** Whether the element has ended (mirrors `el.ended`). Suppresses benign buffering. */
|
|
20
|
+
Ended: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Pure transition function for the media playback state machine.
|
|
24
|
+
*
|
|
25
|
+
* Given the native media event, the current high-level state, and a small context read
|
|
26
|
+
* off the element, returns the next {@link MediaPlaybackState}. This is the single source
|
|
27
|
+
* of truth for the component's state — the component binds native events to this reducer
|
|
28
|
+
* and reflects the result; it never invents transitions of its own.
|
|
29
|
+
*
|
|
30
|
+
* Design rules baked in here:
|
|
31
|
+
* - `error` always wins (an errored element can still emit stray `suspend`/`stalled`).
|
|
32
|
+
* - `seeking` → `buffering` so a ±30s skip / scrub into un-buffered territory shows the
|
|
33
|
+
* indicator during the seek; `seeked` clears it only when the element actually has data
|
|
34
|
+
* (else we stay `buffering` until `canplay`).
|
|
35
|
+
* - `stalled` / `suspend` are CONSERVATIVE: they only buffer when the user is actively
|
|
36
|
+
* trying to play (not paused, not ended) — `suspend` fires benignly after a normal load,
|
|
37
|
+
* and we must not flash a spinner then.
|
|
38
|
+
* - `playing` always clears loading/buffering (the element resumed on its own after a seek).
|
|
39
|
+
*
|
|
40
|
+
* @param event the native media event that fired
|
|
41
|
+
* @param current the current high-level playback state
|
|
42
|
+
* @param ctx element-derived context (play intent, readyState, ended)
|
|
43
|
+
* @returns the next playback state
|
|
44
|
+
*/
|
|
45
|
+
export declare function nextPlaybackState(event: MediaStateEvent, current: MediaPlaybackState, ctx: MediaStateContext): MediaPlaybackState;
|
|
46
|
+
//# sourceMappingURL=playback-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playback-state.d.ts","sourceRoot":"","sources":["../../../src/lib/media-player/playback-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE3D;;;GAGG;AACH,MAAM,MAAM,eAAe,GACvB,WAAW,GACX,gBAAgB,GAChB,SAAS,GACT,gBAAgB,GAChB,SAAS,GACT,SAAS,GACT,QAAQ,GACR,SAAS,GACT,MAAM,GACN,OAAO,GACP,OAAO,GACP,OAAO,GACP,SAAS,GACT,SAAS,GACT,SAAS,CAAC;AAEd;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC;uFACmF;IACnF,SAAS,EAAE,OAAO,CAAC;IACnB;wFACoF;IACpF,UAAU,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,KAAK,EAAE,OAAO,CAAC;CAChB;AAKD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,kBAAkB,EAC3B,GAAG,EAAE,iBAAiB,GACrB,kBAAkB,CAyEpB"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/** `HTMLMediaElement.HAVE_FUTURE_DATA` — enough to advance at least one frame. */
|
|
2
|
+
const HAVE_FUTURE_DATA = 3;
|
|
3
|
+
/**
|
|
4
|
+
* Pure transition function for the media playback state machine.
|
|
5
|
+
*
|
|
6
|
+
* Given the native media event, the current high-level state, and a small context read
|
|
7
|
+
* off the element, returns the next {@link MediaPlaybackState}. This is the single source
|
|
8
|
+
* of truth for the component's state — the component binds native events to this reducer
|
|
9
|
+
* and reflects the result; it never invents transitions of its own.
|
|
10
|
+
*
|
|
11
|
+
* Design rules baked in here:
|
|
12
|
+
* - `error` always wins (an errored element can still emit stray `suspend`/`stalled`).
|
|
13
|
+
* - `seeking` → `buffering` so a ±30s skip / scrub into un-buffered territory shows the
|
|
14
|
+
* indicator during the seek; `seeked` clears it only when the element actually has data
|
|
15
|
+
* (else we stay `buffering` until `canplay`).
|
|
16
|
+
* - `stalled` / `suspend` are CONSERVATIVE: they only buffer when the user is actively
|
|
17
|
+
* trying to play (not paused, not ended) — `suspend` fires benignly after a normal load,
|
|
18
|
+
* and we must not flash a spinner then.
|
|
19
|
+
* - `playing` always clears loading/buffering (the element resumed on its own after a seek).
|
|
20
|
+
*
|
|
21
|
+
* @param event the native media event that fired
|
|
22
|
+
* @param current the current high-level playback state
|
|
23
|
+
* @param ctx element-derived context (play intent, readyState, ended)
|
|
24
|
+
* @returns the next playback state
|
|
25
|
+
*/
|
|
26
|
+
export function nextPlaybackState(event, current, ctx) {
|
|
27
|
+
// `error` is terminal until a new source resets via `emptied`.
|
|
28
|
+
if (current === 'error' && event !== 'emptied' && event !== 'loadstart') {
|
|
29
|
+
return 'error';
|
|
30
|
+
}
|
|
31
|
+
switch (event) {
|
|
32
|
+
case 'error':
|
|
33
|
+
return 'error';
|
|
34
|
+
case 'emptied':
|
|
35
|
+
// Track switch / source cleared — back to the start of the lifecycle.
|
|
36
|
+
return 'loading';
|
|
37
|
+
case 'loadstart':
|
|
38
|
+
return 'loading';
|
|
39
|
+
case 'loadedmetadata':
|
|
40
|
+
// Metadata is in but we may not have playable data yet — stay in loading until
|
|
41
|
+
// `canplay`. Don't regress out of a more-advanced state if it arrives late.
|
|
42
|
+
return current === 'idle' || current === 'loading' ? 'loading' : current;
|
|
43
|
+
case 'canplay':
|
|
44
|
+
case 'canplaythrough':
|
|
45
|
+
// Enough data buffered to play — clear loading/buffering.
|
|
46
|
+
return ctx.IsPlaying ? 'playing' : 'ready';
|
|
47
|
+
case 'waiting':
|
|
48
|
+
// Ran out of buffered data mid-playback.
|
|
49
|
+
return 'buffering';
|
|
50
|
+
case 'seeking':
|
|
51
|
+
// Seeking into (possibly) un-buffered territory — show the indicator during the seek.
|
|
52
|
+
return 'buffering';
|
|
53
|
+
case 'seeked':
|
|
54
|
+
// Clear buffering only if the element now has playable data; else wait for `canplay`.
|
|
55
|
+
if (ctx.ReadyState >= HAVE_FUTURE_DATA) {
|
|
56
|
+
return ctx.IsPlaying ? 'playing' : 'ready';
|
|
57
|
+
}
|
|
58
|
+
return 'buffering';
|
|
59
|
+
case 'playing':
|
|
60
|
+
// Authoritative "we are now playing" — clears loading + buffering.
|
|
61
|
+
return 'playing';
|
|
62
|
+
case 'play':
|
|
63
|
+
// Play was requested; if we're already past loading keep showing the right state,
|
|
64
|
+
// otherwise the load/buffer indicator stays until data arrives.
|
|
65
|
+
if (current === 'loading' || current === 'buffering') {
|
|
66
|
+
return current;
|
|
67
|
+
}
|
|
68
|
+
return 'playing';
|
|
69
|
+
case 'pause':
|
|
70
|
+
// Don't override a terminal ended state with paused.
|
|
71
|
+
return ctx.Ended ? 'ended' : 'paused';
|
|
72
|
+
case 'ended':
|
|
73
|
+
return 'ended';
|
|
74
|
+
case 'stalled':
|
|
75
|
+
case 'suspend':
|
|
76
|
+
// Conservative: only treat as buffering when actively trying to play. `suspend`
|
|
77
|
+
// fires benignly after a normal metadata load — must not flash a spinner then.
|
|
78
|
+
if (ctx.IsPlaying && !ctx.Ended) {
|
|
79
|
+
return 'buffering';
|
|
80
|
+
}
|
|
81
|
+
return current;
|
|
82
|
+
default:
|
|
83
|
+
return current;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=playback-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playback-state.js","sourceRoot":"","sources":["../../../src/lib/media-player/playback-state.ts"],"names":[],"mappings":"AAuCA,kFAAkF;AAClF,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAE3B;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAsB,EACtB,OAA2B,EAC3B,GAAsB;IAEtB,+DAA+D;IAC/D,IAAI,OAAO,KAAK,OAAO,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,WAAW,EAAE,CAAC;QACxE,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,OAAO;YACV,OAAO,OAAO,CAAC;QAEjB,KAAK,SAAS;YACZ,sEAAsE;YACtE,OAAO,SAAS,CAAC;QAEnB,KAAK,WAAW;YACd,OAAO,SAAS,CAAC;QAEnB,KAAK,gBAAgB;YACnB,+EAA+E;YAC/E,4EAA4E;YAC5E,OAAO,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;QAE3E,KAAK,SAAS,CAAC;QACf,KAAK,gBAAgB;YACnB,0DAA0D;YAC1D,OAAO,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;QAE7C,KAAK,SAAS;YACZ,yCAAyC;YACzC,OAAO,WAAW,CAAC;QAErB,KAAK,SAAS;YACZ,sFAAsF;YACtF,OAAO,WAAW,CAAC;QAErB,KAAK,QAAQ;YACX,sFAAsF;YACtF,IAAI,GAAG,CAAC,UAAU,IAAI,gBAAgB,EAAE,CAAC;gBACvC,OAAO,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;YAC7C,CAAC;YACD,OAAO,WAAW,CAAC;QAErB,KAAK,SAAS;YACZ,mEAAmE;YACnE,OAAO,SAAS,CAAC;QAEnB,KAAK,MAAM;YACT,kFAAkF;YAClF,gEAAgE;YAChE,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;gBACrD,OAAO,OAAO,CAAC;YACjB,CAAC;YACD,OAAO,SAAS,CAAC;QAEnB,KAAK,OAAO;YACV,qDAAqD;YACrD,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;QAExC,KAAK,OAAO;YACV,OAAO,OAAO,CAAC;QAEjB,KAAK,SAAS,CAAC;QACf,KAAK,SAAS;YACZ,gFAAgF;YAChF,+EAA+E;YAC/E,IAAI,GAAG,CAAC,SAAS,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;gBAChC,OAAO,WAAW,CAAC;YACrB,CAAC;YACD,OAAO,OAAO,CAAC;QAEjB;YACE,OAAO,OAAO,CAAC;IACnB,CAAC;AACH,CAAC","sourcesContent":["import { MediaPlaybackState } from '../media-player.types';\n\n/**\n * The native HTMLMediaElement event names that drive the {@link MediaPlaybackState}\n * machine. We bind exactly these on the PRIMARY media element.\n */\nexport type MediaStateEvent =\n | 'loadstart'\n | 'loadedmetadata'\n | 'canplay'\n | 'canplaythrough'\n | 'waiting'\n | 'seeking'\n | 'seeked'\n | 'playing'\n | 'play'\n | 'pause'\n | 'ended'\n | 'error'\n | 'stalled'\n | 'suspend'\n | 'emptied';\n\n/**\n * Context the reducer needs beyond the raw event + current state. These are read off the\n * media element at the moment the event fires (kept as plain inputs so the reducer stays\n * a pure, fully unit-testable function with no DOM dependency).\n */\nexport interface MediaStateContext {\n /** Whether the user intends playback (the element is not paused). Drives ready-vs-playing\n * and whether conservative events (`stalled`/`suspend`) should show buffering. */\n IsPlaying: boolean;\n /** The element's `readyState` (HAVE_NOTHING=0 … HAVE_ENOUGH_DATA=4). Used by `seeked` to\n * decide whether we can clear buffering immediately or must wait for `canplay`. */\n ReadyState: number;\n /** Whether the element has ended (mirrors `el.ended`). Suppresses benign buffering. */\n Ended: boolean;\n}\n\n/** `HTMLMediaElement.HAVE_FUTURE_DATA` — enough to advance at least one frame. */\nconst HAVE_FUTURE_DATA = 3;\n\n/**\n * Pure transition function for the media playback state machine.\n *\n * Given the native media event, the current high-level state, and a small context read\n * off the element, returns the next {@link MediaPlaybackState}. This is the single source\n * of truth for the component's state — the component binds native events to this reducer\n * and reflects the result; it never invents transitions of its own.\n *\n * Design rules baked in here:\n * - `error` always wins (an errored element can still emit stray `suspend`/`stalled`).\n * - `seeking` → `buffering` so a ±30s skip / scrub into un-buffered territory shows the\n * indicator during the seek; `seeked` clears it only when the element actually has data\n * (else we stay `buffering` until `canplay`).\n * - `stalled` / `suspend` are CONSERVATIVE: they only buffer when the user is actively\n * trying to play (not paused, not ended) — `suspend` fires benignly after a normal load,\n * and we must not flash a spinner then.\n * - `playing` always clears loading/buffering (the element resumed on its own after a seek).\n *\n * @param event the native media event that fired\n * @param current the current high-level playback state\n * @param ctx element-derived context (play intent, readyState, ended)\n * @returns the next playback state\n */\nexport function nextPlaybackState(\n event: MediaStateEvent,\n current: MediaPlaybackState,\n ctx: MediaStateContext,\n): MediaPlaybackState {\n // `error` is terminal until a new source resets via `emptied`.\n if (current === 'error' && event !== 'emptied' && event !== 'loadstart') {\n return 'error';\n }\n\n switch (event) {\n case 'error':\n return 'error';\n\n case 'emptied':\n // Track switch / source cleared — back to the start of the lifecycle.\n return 'loading';\n\n case 'loadstart':\n return 'loading';\n\n case 'loadedmetadata':\n // Metadata is in but we may not have playable data yet — stay in loading until\n // `canplay`. Don't regress out of a more-advanced state if it arrives late.\n return current === 'idle' || current === 'loading' ? 'loading' : current;\n\n case 'canplay':\n case 'canplaythrough':\n // Enough data buffered to play — clear loading/buffering.\n return ctx.IsPlaying ? 'playing' : 'ready';\n\n case 'waiting':\n // Ran out of buffered data mid-playback.\n return 'buffering';\n\n case 'seeking':\n // Seeking into (possibly) un-buffered territory — show the indicator during the seek.\n return 'buffering';\n\n case 'seeked':\n // Clear buffering only if the element now has playable data; else wait for `canplay`.\n if (ctx.ReadyState >= HAVE_FUTURE_DATA) {\n return ctx.IsPlaying ? 'playing' : 'ready';\n }\n return 'buffering';\n\n case 'playing':\n // Authoritative \"we are now playing\" — clears loading + buffering.\n return 'playing';\n\n case 'play':\n // Play was requested; if we're already past loading keep showing the right state,\n // otherwise the load/buffer indicator stays until data arrives.\n if (current === 'loading' || current === 'buffering') {\n return current;\n }\n return 'playing';\n\n case 'pause':\n // Don't override a terminal ended state with paused.\n return ctx.Ended ? 'ended' : 'paused';\n\n case 'ended':\n return 'ended';\n\n case 'stalled':\n case 'suspend':\n // Conservative: only treat as buffering when actively trying to play. `suspend`\n // fires benignly after a normal metadata load — must not flash a spinner then.\n if (ctx.IsPlaying && !ctx.Ended) {\n return 'buffering';\n }\n return current;\n\n default:\n return current;\n }\n}\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure layout/visibility logic for the media player's transcript panel.
|
|
3
|
+
*
|
|
4
|
+
* Kept framework-agnostic and side-effect-free so the rules (default-below,
|
|
5
|
+
* show/hide toggle, effective visibility) are independently unit-testable.
|
|
6
|
+
*/
|
|
7
|
+
/** Where the transcript panel sits relative to the media. */
|
|
8
|
+
export type TranscriptPosition = 'side' | 'bottom';
|
|
9
|
+
/**
|
|
10
|
+
* Resolves whether the transcript panel should actually render, combining:
|
|
11
|
+
* - whether a transcript exists at all (`hasTranscript`),
|
|
12
|
+
* - the consumer's `ShowTranscript` master switch,
|
|
13
|
+
* - the user's runtime show/hide toggle state (`userVisible`).
|
|
14
|
+
*
|
|
15
|
+
* All three must be truthy for the panel to render. When no transcript exists
|
|
16
|
+
* (or `ShowTranscript` is false) the runtime toggle is irrelevant.
|
|
17
|
+
*
|
|
18
|
+
* @param hasTranscript whether one or more transcript cues are present
|
|
19
|
+
* @param showTranscript the `ShowTranscript` master input
|
|
20
|
+
* @param userVisible the runtime (toggle-driven) visibility state
|
|
21
|
+
* @returns true when the transcript panel should render
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveTranscriptVisible(hasTranscript: boolean, showTranscript: boolean, userVisible: boolean): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Resolves whether the show/hide toggle button should render. The toggle only
|
|
26
|
+
* makes sense when there is a transcript to toggle AND the consumer opted into
|
|
27
|
+
* showing it (`ShowTranscript`) AND the consumer enabled the toggle control.
|
|
28
|
+
*
|
|
29
|
+
* @param hasTranscript whether one or more transcript cues are present
|
|
30
|
+
* @param showTranscript the `ShowTranscript` master input
|
|
31
|
+
* @param showToggle the `ShowTranscriptToggle` input (whether the button renders)
|
|
32
|
+
* @returns true when the toggle button should render
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveTranscriptToggleVisible(hasTranscript: boolean, showTranscript: boolean, showToggle: boolean): boolean;
|
|
35
|
+
//# sourceMappingURL=transcript-layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript-layout.d.ts","sourceRoot":"","sources":["../../../src/lib/media-player/transcript-layout.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,6DAA6D;AAC7D,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEnD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,OAAO,EACtB,cAAc,EAAE,OAAO,EACvB,WAAW,EAAE,OAAO,GACnB,OAAO,CAET;AAED;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAC5C,aAAa,EAAE,OAAO,EACtB,cAAc,EAAE,OAAO,EACvB,UAAU,EAAE,OAAO,GAClB,OAAO,CAET"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure layout/visibility logic for the media player's transcript panel.
|
|
3
|
+
*
|
|
4
|
+
* Kept framework-agnostic and side-effect-free so the rules (default-below,
|
|
5
|
+
* show/hide toggle, effective visibility) are independently unit-testable.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Resolves whether the transcript panel should actually render, combining:
|
|
9
|
+
* - whether a transcript exists at all (`hasTranscript`),
|
|
10
|
+
* - the consumer's `ShowTranscript` master switch,
|
|
11
|
+
* - the user's runtime show/hide toggle state (`userVisible`).
|
|
12
|
+
*
|
|
13
|
+
* All three must be truthy for the panel to render. When no transcript exists
|
|
14
|
+
* (or `ShowTranscript` is false) the runtime toggle is irrelevant.
|
|
15
|
+
*
|
|
16
|
+
* @param hasTranscript whether one or more transcript cues are present
|
|
17
|
+
* @param showTranscript the `ShowTranscript` master input
|
|
18
|
+
* @param userVisible the runtime (toggle-driven) visibility state
|
|
19
|
+
* @returns true when the transcript panel should render
|
|
20
|
+
*/
|
|
21
|
+
export function resolveTranscriptVisible(hasTranscript, showTranscript, userVisible) {
|
|
22
|
+
return hasTranscript && showTranscript && userVisible;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Resolves whether the show/hide toggle button should render. The toggle only
|
|
26
|
+
* makes sense when there is a transcript to toggle AND the consumer opted into
|
|
27
|
+
* showing it (`ShowTranscript`) AND the consumer enabled the toggle control.
|
|
28
|
+
*
|
|
29
|
+
* @param hasTranscript whether one or more transcript cues are present
|
|
30
|
+
* @param showTranscript the `ShowTranscript` master input
|
|
31
|
+
* @param showToggle the `ShowTranscriptToggle` input (whether the button renders)
|
|
32
|
+
* @returns true when the toggle button should render
|
|
33
|
+
*/
|
|
34
|
+
export function resolveTranscriptToggleVisible(hasTranscript, showTranscript, showToggle) {
|
|
35
|
+
return hasTranscript && showTranscript && showToggle;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=transcript-layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript-layout.js","sourceRoot":"","sources":["../../../src/lib/media-player/transcript-layout.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,wBAAwB,CACtC,aAAsB,EACtB,cAAuB,EACvB,WAAoB;IAEpB,OAAO,aAAa,IAAI,cAAc,IAAI,WAAW,CAAC;AACxD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,8BAA8B,CAC5C,aAAsB,EACtB,cAAuB,EACvB,UAAmB;IAEnB,OAAO,aAAa,IAAI,cAAc,IAAI,UAAU,CAAC;AACvD,CAAC","sourcesContent":["/**\n * Pure layout/visibility logic for the media player's transcript panel.\n *\n * Kept framework-agnostic and side-effect-free so the rules (default-below,\n * show/hide toggle, effective visibility) are independently unit-testable.\n */\n\n/** Where the transcript panel sits relative to the media. */\nexport type TranscriptPosition = 'side' | 'bottom';\n\n/**\n * Resolves whether the transcript panel should actually render, combining:\n * - whether a transcript exists at all (`hasTranscript`),\n * - the consumer's `ShowTranscript` master switch,\n * - the user's runtime show/hide toggle state (`userVisible`).\n *\n * All three must be truthy for the panel to render. When no transcript exists\n * (or `ShowTranscript` is false) the runtime toggle is irrelevant.\n *\n * @param hasTranscript whether one or more transcript cues are present\n * @param showTranscript the `ShowTranscript` master input\n * @param userVisible the runtime (toggle-driven) visibility state\n * @returns true when the transcript panel should render\n */\nexport function resolveTranscriptVisible(\n hasTranscript: boolean,\n showTranscript: boolean,\n userVisible: boolean,\n): boolean {\n return hasTranscript && showTranscript && userVisible;\n}\n\n/**\n * Resolves whether the show/hide toggle button should render. The toggle only\n * makes sense when there is a transcript to toggle AND the consumer opted into\n * showing it (`ShowTranscript`) AND the consumer enabled the toggle control.\n *\n * @param hasTranscript whether one or more transcript cues are present\n * @param showTranscript the `ShowTranscript` master input\n * @param showToggle the `ShowTranscriptToggle` input (whether the button renders)\n * @returns true when the toggle button should render\n */\nexport function resolveTranscriptToggleVisible(\n hasTranscript: boolean,\n showTranscript: boolean,\n showToggle: boolean,\n): boolean {\n return hasTranscript && showTranscript && showToggle;\n}\n"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, framework-agnostic waveform math for the media player.
|
|
3
|
+
*
|
|
4
|
+
* These helpers turn a raw mono PCM channel (a `Float32Array` of sample amplitudes,
|
|
5
|
+
* typically in the `[-1, 1]` range that the Web Audio API produces) into a small,
|
|
6
|
+
* fixed-size array of normalized `0..1` peak values — one per rendered waveform bar.
|
|
7
|
+
*
|
|
8
|
+
* They carry no DOM / Angular / `AudioContext` dependency so they can be unit-tested
|
|
9
|
+
* in isolation, mirroring how `cue-utils.ts` splits the cue math out of the component.
|
|
10
|
+
*/
|
|
11
|
+
/** How each downsample bucket is reduced to a single amplitude. */
|
|
12
|
+
export type WaveformAggregation = 'max-abs' | 'rms';
|
|
13
|
+
/** The default number of bars a waveform renders at. */
|
|
14
|
+
export declare const DEFAULT_WAVEFORM_BARS = 160;
|
|
15
|
+
/**
|
|
16
|
+
* Downsamples a raw mono sample buffer into `barCount` per-bucket amplitudes and
|
|
17
|
+
* normalizes the result to `0..1` (the loudest bucket becomes `1`).
|
|
18
|
+
*
|
|
19
|
+
* The samples are partitioned into `barCount` contiguous, (near-)equal buckets in
|
|
20
|
+
* index order; each bucket is reduced to one amplitude via `aggregation`:
|
|
21
|
+
* - `'max-abs'` (default) — the maximum absolute sample in the bucket (crisp peaks),
|
|
22
|
+
* - `'rms'` — the root-mean-square of the bucket (smoother, energy-weighted).
|
|
23
|
+
*
|
|
24
|
+
* Edge cases:
|
|
25
|
+
* - empty / null input → an array of `barCount` zeros,
|
|
26
|
+
* - fewer samples than bars → every sample still lands in a bucket; trailing empty
|
|
27
|
+
* buckets are `0` (no out-of-bounds reads),
|
|
28
|
+
* - a silent or constant-zero buffer → all zeros (normalization is skipped when the
|
|
29
|
+
* peak is `0`, so we never divide by zero).
|
|
30
|
+
*
|
|
31
|
+
* @param samples raw mono channel data (e.g. `AudioBuffer.getChannelData(0)`)
|
|
32
|
+
* @param barCount number of output bars (clamped to `>= 1`)
|
|
33
|
+
* @param aggregation per-bucket reducer (default `'max-abs'`)
|
|
34
|
+
* @returns a `barCount`-length array of normalized `0..1` peaks
|
|
35
|
+
*/
|
|
36
|
+
export declare function downsamplePeaks(samples: Float32Array | number[] | null | undefined, barCount?: number, aggregation?: WaveformAggregation): number[];
|
|
37
|
+
/**
|
|
38
|
+
* Normalizes an array of non-negative amplitudes to `0..1` by dividing by the maximum.
|
|
39
|
+
* Returns a copy. When the maximum is `0` (silence) or non-finite, returns the values
|
|
40
|
+
* clamped to `[0, 1]` without scaling (so an all-zero input stays all-zero).
|
|
41
|
+
*/
|
|
42
|
+
export declare function normalizePeaks(values: number[]): number[];
|
|
43
|
+
/** Clamps a number to the `[0, 1]` range. */
|
|
44
|
+
export declare function clamp01(v: number): number;
|
|
45
|
+
//# sourceMappingURL=waveform-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waveform-utils.d.ts","sourceRoot":"","sources":["../../../src/lib/media-player/waveform-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,mEAAmE;AACnE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,KAAK,CAAC;AAEpD,wDAAwD;AACxD,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,YAAY,GAAG,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,EACnD,QAAQ,GAAE,MAA8B,EACxC,WAAW,GAAE,mBAA+B,GAC3C,MAAM,EAAE,CASV;AAoDD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAYzD;AAED,6CAA6C;AAC7C,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAQzC"}
|