@@ -1466,88 +1466,158 @@ function setupSceneRotationTranslationFields(event = null) {
14661466
14671467function setupGenerateMesh ( ) {
14681468 const generateMeshButton = document . getElementById ( "generate_mesh" ) ;
1469+ const saveButton = document . getElementById ( "save" ) ;
1470+ const mapInput = document . getElementById ( "id_map" ) ;
1471+
1472+ saveButton ?. addEventListener ( "click" , async ( e ) => {
1473+ const allowedExtensions = [ "mp4" , "mov" , "avi" , "webm" , "mkv" ] ;
1474+
1475+ const file = mapInput ?. files ?. [ 0 ] ;
1476+ if ( ! file ) {
1477+ // No file selected; nothing to validate here.
1478+ return ;
1479+ }
1480+ const extension = file . name . split ( "." ) . pop ( ) . toLowerCase ( ) ;
1481+
1482+ const isVideoMime = file . type . startsWith ( "video/" ) ;
1483+ const isVideoExt = allowedExtensions . includes ( extension ) ;
1484+
1485+ if ( isVideoMime && isVideoExt ) {
1486+ e . preventDefault ( ) ;
1487+ alert ( "Please click generate mesh when uploading video file." ) ;
1488+ return ;
1489+ }
1490+ } ) ;
1491+
14691492 if ( ! generateMeshButton ) return ;
14701493
14711494 // Start monitoring mapping service status
14721495 startMappingServiceStatusMonitoring ( ) ;
14731496
1474- generateMeshButton . addEventListener ( "click" , async function ( ) {
1497+ generateMeshButton ?. addEventListener ( "click" , async ( e ) => {
1498+ e . preventDefault ( ) ;
1499+
14751500 const sceneId = document . getElementById ( "sceneUID" ) ?. value ;
1476- if ( ! sceneId ) {
1477- alert ( "Scene ID not found" ) ;
1478- return ;
1479- }
1501+ const form = document . getElementById ( "scene_update_form" ) ;
1502+
1503+ if ( ! sceneId ) return alert ( "Scene ID not found" ) ;
1504+ if ( ! form ) return alert ( "Form not found" ) ;
14801505
14811506 // Show loading state
14821507 const spinner = document . getElementById ( "mesh_spinner" ) ;
1483- spinner . classList . remove ( "d-none" ) ;
1508+
1509+ spinner ?. classList . remove ( "d-none" ) ;
1510+ generateMeshButton . dataset . meshRunning = "1" ;
14841511 generateMeshButton . disabled = true ;
14851512
14861513 try {
1487- await generateMeshFromCameras ( sceneId ) ;
1488- } catch ( error ) {
1489- console . error ( "Mesh generation failed:" , error ) ;
1490- alert ( "Mesh generation failed: " + error . message ) ;
1514+ const startResult = await generateMeshFromCameras ( sceneId , form ) ;
1515+
1516+ const requestId = startResult . request_id ;
1517+ if ( ! requestId ) {
1518+ throw new Error ( "Backend did not return request_id" ) ;
1519+ }
1520+
1521+ await pollMeshStatus ( sceneId , requestId ) ;
1522+
1523+ alert ( "Mesh generated successfully! The scene map has been updated." ) ;
1524+
1525+ $ ( "#id_rotation_x" ) . val ( 0 ) ;
1526+ $ ( "#id_rotation_y" ) . val ( 0 ) ;
1527+ $ ( "#id_rotation_z" ) . val ( 0 ) ;
1528+ $ ( "#id_translation_x" ) . val ( 0 ) ;
1529+ $ ( "#id_translation_y" ) . val ( 0 ) ;
1530+ $ ( "#id_translation_z" ) . val ( 0 ) ;
1531+ window . location . reload ( ) ;
1532+ } catch ( err ) {
1533+ console . error ( err ) ;
1534+ alert ( "Mesh generation failed: " + ( err ?. message ?? String ( err ) ) ) ;
14911535 } finally {
14921536 // Hide loading state
1493- spinner . classList . add ( "d-none" ) ;
1537+ spinner ?. classList . add ( "d-none" ) ;
1538+ generateMeshButton . dataset . meshRunning = "0" ;
14941539 generateMeshButton . disabled = false ;
14951540 }
14961541 } ) ;
14971542}
14981543
1499- async function generateMeshFromCameras ( sceneId ) {
1500- const tokenElement = document . getElementById ( "auth-token" ) ;
1501- if ( ! tokenElement ) {
1502- throw new Error ( "Authentication token not found" ) ;
1503- }
1544+ async function pollMeshStatus ( sceneId , requestId ) {
1545+ const timeout = 10 * 60 * 1000 ; // 10 minutes
1546+ const start = Date . now ( ) ;
15041547
1505- const authToken = `Token ${ tokenElement . value } ` ;
1506- try {
1507- const response = await fetch ( `/scene/generate-mesh/${ sceneId } /` , {
1508- method : "POST" ,
1509- headers : {
1510- "Content-Type" : "application/json" ,
1511- Authorization : authToken ,
1512- "X-CSRFToken" : document . querySelector ( "[name=csrfmiddlewaretoken]" )
1513- ?. value ,
1514- } ,
1515- body : JSON . stringify ( {
1516- mesh_type : "mesh" ,
1517- } ) ,
1518- } ) ;
1548+ while ( true ) {
1549+ if ( Date . now ( ) - start > timeout ) {
1550+ throw new Error ( "Timed out waiting for mesh generation." ) ;
1551+ }
15191552
1520- // Log response for debugging
1521- console . log ( "Response status:" , response . status ) ;
1522- console . log ( "Response headers:" , response . headers ) ;
1553+ const resp = await fetch (
1554+ `/scene/generate-mesh- status/ ${ sceneId } /?request_id= ${ encodeURIComponent ( requestId ) } ` ,
1555+ ) ;
15231556
1524- if ( ! response . ok ) {
1525- const errorText = await response . text ( ) ;
1526- console . log ( "Error response text:" , errorText ) ;
1527- throw new Error ( `HTTP ${ response . status } : ${ errorText } ` ) ;
1557+ const data = await resp . json ( ) ;
1558+
1559+ if ( ! resp . ok ) {
1560+ throw new Error ( data ?. error || "Status check failed" ) ;
15281561 }
15291562
1530- const result = await response . json ( ) ;
1563+ if ( data . success === false ) {
1564+ throw new Error ( data ?. error || "Mesh generation failed" ) ;
1565+ }
15311566
1532- if ( result . success ) {
1533- alert ( "Mesh generated successfully! The scene map has been updated." ) ;
1534- // Optionally reload the page to show the updated map
1535- // window.location.reload();
1536- // Set rotation and translation fields to zero
1537- $ ( "#id_rotation_x" ) . val ( 0 ) ;
1538- $ ( "#id_rotation_y" ) . val ( 0 ) ;
1539- $ ( "#id_rotation_z" ) . val ( 0 ) ;
1540- $ ( "#id_translation_x" ) . val ( 0 ) ;
1541- $ ( "#id_translation_y" ) . val ( 0 ) ;
1542- $ ( "#id_translation_z" ) . val ( 0 ) ;
1543- } else {
1544- throw new Error ( result . message || "Mesh generation failed" ) ;
1567+ if ( data . state === "complete" ) {
1568+ return data ;
15451569 }
1546- } catch ( error ) {
1547- throw error ;
1570+
1571+ if ( data . state === "failed" ) {
1572+ throw new Error ( data . error || "Mesh generation failed" ) ;
1573+ }
1574+
1575+ // Wait before next poll
1576+ await new Promise ( ( r ) => setTimeout ( r , 1500 ) ) ;
15481577 }
15491578}
15501579
1580+ async function generateMeshFromCameras ( sceneId , form ) {
1581+ const url = `/scene/generate-mesh/${ sceneId } /` ;
1582+
1583+ const formData = new FormData ( form ) ;
1584+
1585+ // Make sure CSRF is sent (Django)
1586+ const csrfToken =
1587+ document . querySelector ( 'input[name="csrfmiddlewaretoken"]' ) ?. value ||
1588+ getCookie ( "csrftoken" ) ;
1589+
1590+ const resp = await fetch ( url , {
1591+ method : "POST" ,
1592+ headers : {
1593+ "X-CSRFToken" : csrfToken ,
1594+ Accept : "application/json" ,
1595+ } ,
1596+ body : formData ,
1597+ } ) ;
1598+
1599+ const data = await resp . json ( ) . catch ( ( ) => ( { } ) ) ;
1600+
1601+ if ( ! resp . ok || data . success === false ) {
1602+ throw new Error (
1603+ data ?. error || `Generate mesh failed (HTTP ${ resp . status } )` ,
1604+ ) ;
1605+ }
1606+
1607+ // expects { success: true, request_id: "..." }
1608+ if ( ! data . request_id ) {
1609+ throw new Error ( "Generate mesh response missing request_id" ) ;
1610+ }
1611+
1612+ return data ;
1613+ }
1614+
1615+ // Optional cookie helper if you don't already have one:
1616+ function getCookie ( name ) {
1617+ const match = document . cookie . match ( new RegExp ( "(^| )" + name + "=([^;]+)" ) ) ;
1618+ return match ? decodeURIComponent ( match [ 2 ] ) : null ;
1619+ }
1620+
15511621async function checkMappingServiceStatus ( ) {
15521622 const generateMeshButton = document . getElementById ( "generate_mesh" ) ;
15531623 if ( ! generateMeshButton ) return ;
@@ -1577,7 +1647,10 @@ async function checkMappingServiceStatus() {
15771647 if ( status . available ) {
15781648 // Service is available, show the button
15791649 generateMeshButton . style . display = "inline-block" ;
1580- generateMeshButton . disabled = false ;
1650+ const running = generateMeshButton . dataset . meshRunning === "1" ;
1651+ if ( ! running ) {
1652+ generateMeshButton . disabled = false ;
1653+ }
15811654 generateMeshButton . title =
15821655 "Generate 3D mesh from camera images using mapping service" ;
15831656
0 commit comments