this repo has no description
at main 152 lines 4.1 kB view raw
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>