this repo has no description
1<script lang="ts">
2 import { createEventDispatcher, onMount, onDestroy } from 'svelte';
3 import { loggerFor } from '@amp/web-apps-logger';
4
5 const logger = loggerFor('components/MotionArtwork');
6
7 type HLSError = {
8 type: string;
9 message: string;
10 details: string;
11 fatal: boolean;
12 handled: boolean;
13 };
14
15 type MotionArtworkError = {
16 type: string;
17 reason: string;
18 fatal: boolean;
19 error?: Error;
20 };
21
22 /** HTML `id` attribute for the <video /> element */
23 export let id: string;
24
25 /** Source URL for the video, an HLS playlist ending in .m3u8 */
26 export let src: string;
27
28 /** Poster image to show while the video is loading */
29 export let poster: string | undefined;
30
31 /** If the video should loop from end to start. */
32 export let loop: boolean = true;
33
34 /** If the audio should be muted on the video. */
35 export let muted: boolean = true;
36
37 /** If the video should be paused when initially loaded. */
38 export let paused: boolean = true;
39
40 /** The constructor to use for creating an Hls playback session. */
41 export let HLS: Window['Hls'] = window.Hls;
42
43 /** RTCReportingAgent instance for RTC reporting on video playback. */
44 export let reportingAgent: any = undefined;
45
46 /** HTMLVideoElement used by HLS.js to render the video */
47 export let videoElement: HTMLVideoElement | null = null;
48
49 /** Internal error state for the component */
50 let errorState: MotionArtworkError | undefined;
51
52 let hlsSession: Window['Hls'] | undefined;
53
54 /** Dispatcher for errors. */
55 const dispatch = createEventDispatcher<{ error: MotionArtworkError }>();
56
57 function handleError(details: MotionArtworkError) {
58 logger.error(
59 `Error playing MotionArtwork with HLS: ${details?.reason}`,
60 details?.error,
61 );
62
63 errorState = {
64 type: details.type,
65 reason: details.reason,
66 fatal: details.fatal,
67 error: details?.error,
68 };
69
70 dispatch('error', errorState);
71 }
72
73 const hlsSupported = HLS?.isSupported() ?? false;
74
75 onMount(function () {
76 if (!hlsSupported) {
77 handleError({
78 type: 'runtime',
79 reason: 'unsupported',
80 fatal: true,
81 });
82 return;
83 }
84
85 // Create a new HLS.js playback session
86 hlsSession = new HLS({
87 debug: false,
88 debugLevel: 'error',
89 enablePerformanceLogging: false,
90 nativeControlsEnabled: false,
91
92 appData: {
93 reportingAgent: reportingAgent,
94 serviceName: reportingAgent?.ServiceName,
95 },
96 });
97
98 hlsSession.on(
99 HLS.Events.ERROR,
100 function (_event: string, error: HLSError) {
101 handleError({
102 type: 'hls',
103 reason: error.message,
104 fatal: error.fatal,
105 error: error as unknown as Error,
106 });
107 },
108 );
109
110 // Direct HLS.js to the VideoElement to use and start loading the video source
111 hlsSession.attachMedia(videoElement);
112 hlsSession.loadSource(src, {
113 /* HLS.js loading options go here */
114 });
115 });
116
117 onDestroy(() => {
118 // Stop the video, release resources, and destroy the HLS context
119 hlsSession?.destroy();
120 });
121</script>
122
123{#if errorState !== undefined}
124 <slot name="error" error={errorState} {poster} />
125{:else}
126 <!-- svelte-ignore a11y-media-has-caption -->
127 <video
128 {id}
129 {loop}
130 {poster}
131 preload="none"
132 data-loop={true}
133 playsinline={true}
134 controls={false}
135 bind:this={videoElement}
136 bind:muted
137 bind:paused
138 on:play
139 on:ended
140 on:loadedmetadata
141 />
142{/if}
143
144<style>
145 video {
146 width: 100%;
147 height: 100%;
148 object-fit: cover;
149 object-position: center center;
150 aspect-ratio: var(--aspect-ratio);
151 }
152</style>