diff --git a/resources/js/modules/editor/partials/canvas/video-download/video-download-modal.jsx b/resources/js/modules/editor/partials/canvas/video-download/video-download-modal.jsx index 32b1c63..2668a1b 100644 --- a/resources/js/modules/editor/partials/canvas/video-download/video-download-modal.jsx +++ b/resources/js/modules/editor/partials/canvas/video-download/video-download-modal.jsx @@ -3,7 +3,7 @@ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } f import { Progress } from '@/components/ui/progress'; import { Textarea } from '@/components/ui/textarea'; import { Clock10Icon, Download, Droplets } from 'lucide-react'; -import { useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; const VideoDownloadModal = ({ nonWatermarkVideosLeft = 0, @@ -17,14 +17,24 @@ const VideoDownloadModal = ({ }) => { const [showDebug, setShowDebug] = useState(false); const [exportType, setExportType] = useState(null); + const [estimatedTimeRemaining, setEstimatedTimeRemaining] = useState(null); + const [status, setStatus] = useState('start'); // 'start', 'processing', 'complete' + + const exportStartTime = useRef(null); + const lastProgressTime = useRef(null); + const lastProgress = useRef(0); const handleExportWithoutWatermark = () => { setExportType('without'); + setEstimatedTimeRemaining(null); + setStatus('processing'); handleDownloadButton(); }; const handleExportWithWatermark = () => { setExportType('with'); + setEstimatedTimeRemaining(null); + setStatus('processing'); handleDownloadButton(); }; @@ -32,10 +42,86 @@ const VideoDownloadModal = ({ onClose(); setTimeout(() => { setExportType(null); + setEstimatedTimeRemaining(null); + setStatus('start'); + exportStartTime.current = null; + lastProgressTime.current = null; + lastProgress.current = 0; }, 300); }; - const isComplete = exportProgress >= 100 && !isExporting; + // Update status based on export progress - transition to complete immediately when 100% + useEffect(() => { + if (status === 'processing' && exportProgress >= 100) { + setStatus('complete'); + } + }, [exportProgress, status]); + + // Calculate estimated time remaining based on progress speed + useEffect(() => { + if (status !== 'processing' || exportProgress === 0) { + exportStartTime.current = null; + lastProgressTime.current = null; + lastProgress.current = 0; + setEstimatedTimeRemaining(null); + return; + } + + // Clear estimate when complete + if (exportProgress >= 100) { + setEstimatedTimeRemaining(0); + return; + } + + const now = Date.now(); + + // Initialize timing on first progress + if (!exportStartTime.current) { + exportStartTime.current = now; + lastProgress.current = exportProgress; + console.log('Initialized timing at', exportProgress + '%'); + return; + } + + // Calculate every time progress changes after initial 3 seconds + const timeSinceStart = now - exportStartTime.current; + const progressDelta = exportProgress - lastProgress.current; + + console.log( + 'Progress:', + exportProgress + '%', + 'Time since start:', + Math.round(timeSinceStart / 1000) + 's', + 'Progress delta:', + progressDelta + '%', + ); + + if (timeSinceStart > 2000 && progressDelta > 0) { + const progressRate = progressDelta / timeSinceStart; // progress per ms + const remainingProgress = 100 - exportProgress; + const estimatedMs = remainingProgress / progressRate; + const estimatedSeconds = Math.round(estimatedMs / 1000); + + console.log('Progress rate:', progressRate, 'Estimated seconds:', estimatedSeconds); + + if (estimatedSeconds >= 1 && estimatedSeconds <= 600) { + setEstimatedTimeRemaining(estimatedSeconds); + console.log('Set estimated time:', estimatedSeconds + 's'); + } else { + console.log('Time estimate out of range:', estimatedSeconds + 's'); + } + } + }, [status, exportProgress]); + + const formatTimeRemaining = (seconds) => { + if (seconds < 60) { + return `~${seconds}s remaining`; + } else { + const minutes = Math.floor(seconds / 60); + const remainingSeconds = seconds % 60; + return `~${minutes}m ${remainingSeconds}s remaining`; + } + }; const exportTimes = [ { @@ -56,18 +142,18 @@ const VideoDownloadModal = ({ ]; return ( - + isExporting && e.preventDefault()} - onEscapeKeyDown={(e) => isExporting && e.preventDefault()} + onInteractOutside={(e) => status === 'processing' && e.preventDefault()} + onEscapeKeyDown={(e) => status === 'processing' && e.preventDefault()} > Export Video - {!isExporting && !isComplete && ( + {status === 'start' && (
@@ -77,7 +163,7 @@ const VideoDownloadModal = ({

{exportTimes.map((exportTime, index) => ( -
+
{exportTime.icon} {exportTime.label} @@ -117,7 +203,7 @@ const VideoDownloadModal = ({
)} - {isExporting && ( + {status === 'processing' && (
@@ -128,7 +214,11 @@ const VideoDownloadModal = ({ )}
-

Exporting {exportType === 'without' ? 'without watermark' : 'with'}

+

Exporting {exportType === 'without' ? 'without watermark' : ''}

+ +

+ Please do not close this window while the export is in progress. +

{exportStatus &&

{exportStatus}

}
@@ -139,9 +229,35 @@ const VideoDownloadModal = ({ {Math.round(exportProgress)}%
+ {estimatedTimeRemaining != null ? ( +
+ {formatTimeRemaining(estimatedTimeRemaining)} +
+ ) : ( + exportProgress > 5 && ( +
+ Calculating time remaining... +
+ ) + )}
)} + + {status === 'complete' && ( +
+
+ +
+
+

Export Complete!

+

Your video has been successfully exported.

+
+ +
+ )}
); diff --git a/vite.config.ts b/vite.config.ts index 34ab181..35bae97 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -16,7 +16,7 @@ export default defineConfig({ ], esbuild: { jsx: 'automatic', - drop: ["console", "debugger"], + //drop: ["console", "debugger"], }, resolve: { alias: {