1- async function fetchNthVideo ( index ) {
2- try {
3- const response = await fetch ( ) ;
4- if ( response . ok ) {
5- const blob = await response . blob ( ) ;
6- return URL . createObjectURL ( blob ) ;
7- }
8- } catch ( error ) { }
9- return null ; // No more videos
10- }
111
122function loadVideo ( src ) {
133 return new Promise ( ( resolve , reject ) => {
144 const video = document . createElement ( 'video' ) ;
5+
6+ // Enhanced mobile video attributes
157 video . autoplay = true ;
168 video . loop = true ;
179 video . muted = true ;
1810 video . playsInline = true ;
19- video . onloadeddata = ( ) => resolve ( video ) ;
20- video . onerror = reject ;
11+ video . controls = false ;
12+ video . preload = 'metadata' ;
13+
14+ // Add webkit-specific attributes for iOS
15+ video . setAttribute ( 'webkit-playsinline' , 'true' ) ;
16+ video . setAttribute ( 'x-webkit-airplay' , 'allow' ) ;
17+
18+ // Enhanced error handling and loading events
19+ video . onloadeddata = ( ) => {
20+ // Force play on mobile devices
21+ video . play ( ) . catch ( ( ) => { } ) ;
22+ resolve ( video ) ;
23+ } ;
24+
25+ video . onerror = ( e ) => {
26+ reject ( e ) ;
27+ } ;
28+
2129 video . src = src ;
2230 } ) ;
2331}
@@ -76,6 +84,17 @@ function spawnVideo(video) {
7684 video . style . top = y + 'px' ;
7785 setBallSize ( video , radius ) ;
7886
87+ // Add additional mobile-specific styling
88+ video . style . zIndex = '1' ;
89+ video . style . display = 'block' ;
90+
91+ // Ensure video plays after being added to DOM
92+ setTimeout ( ( ) => {
93+ if ( video . paused ) {
94+ video . play ( ) . catch ( ( ) => { } ) ;
95+ }
96+ } , 100 ) ;
97+
7998 physicsData . push ( {
8099 x : x ,
81100 y : y ,
@@ -92,12 +111,14 @@ function spawnVideo(video) {
92111let currentIndex = 0 ;
93112
94113async function startNextDownload ( i ) {
95- loadVideo ( `/api/videos/${ i } ` )
96- . then ( spawnVideo )
97- . then ( function ( ) { setTimeout ( function ( ) { startNextDownload ( i + 1 ) ; } , 2000 ) ; } )
98- . catch ( ( error ) => {
99- setTimeout ( function ( ) { startNextDownload ( i ) ; } , 2000 ) ;
100- } ) ;
114+ try {
115+ const video = await loadVideo ( `/api/videos/${ i } ` ) ;
116+ spawnVideo ( video ) ;
117+ setTimeout ( ( ) => startNextDownload ( i + 1 ) , 2000 ) ;
118+ } catch ( error ) {
119+ // Always retry the same video after 2 seconds
120+ setTimeout ( ( ) => startNextDownload ( i ) , 2000 ) ;
121+ }
101122}
102123
103124function updatePhysics ( ) {
@@ -261,6 +282,39 @@ function updateBallRadius() {
261282
262283window . addEventListener ( 'resize' , updateBallRadius ) ;
263284
285+ // Mobile-specific initialization
286+ function initializeMobileSupport ( ) {
287+ // Add touch event to enable video playback on mobile
288+ const enableVideoPlayback = ( ) => {
289+ // Try to play any existing videos
290+ const videos = document . querySelectorAll ( 'video' ) ;
291+ videos . forEach ( video => {
292+ if ( video . paused ) {
293+ video . play ( ) . catch ( ( ) => { } ) ;
294+ }
295+ } ) ;
296+ // Remove the event listeners after first interaction
297+ document . removeEventListener ( 'touchstart' , enableVideoPlayback ) ;
298+ document . removeEventListener ( 'click' , enableVideoPlayback ) ;
299+ } ;
300+
301+ // Listen for first user interaction
302+ document . addEventListener ( 'touchstart' , enableVideoPlayback , { once : true } ) ;
303+ document . addEventListener ( 'click' , enableVideoPlayback , { once : true } ) ;
304+
305+ // Prevent default touch behaviors that might interfere
306+ document . addEventListener ( 'touchmove' , ( e ) => {
307+ e . preventDefault ( ) ;
308+ } , { passive : false } ) ;
309+
310+ document . addEventListener ( 'touchend' , ( e ) => {
311+ e . preventDefault ( ) ;
312+ } , { passive : false } ) ;
313+ }
314+
315+ // Initialize mobile support
316+ initializeMobileSupport ( ) ;
317+
264318// Initialize the app
265319startNextDownload ( 0 ) ;
266320setInterval ( updatePhysics , 16 ) ;
0 commit comments