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 edc6aa1..19827f3 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
@@ -1,10 +1,11 @@
import { Button } from '@/components/ui/button';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Progress } from '@/components/ui/progress';
+import { Spinner } from '@/components/ui/spinner.js';
import { Textarea } from '@/components/ui/textarea';
+import useUserStore from '@/stores/UserStore';
import { Clock10Icon, Download, Droplets } from 'lucide-react';
import { useEffect, useRef, useState } from 'react';
-import useUserStore from '@/stores/UserStore';
const VideoDownloadModal = ({
nonWatermarkVideosLeft = 0,
@@ -15,31 +16,94 @@ const VideoDownloadModal = ({
isExporting,
exportProgress,
exportStatus,
+ videoBlob,
+ videoBlobFilename,
}) => {
const [showDebug, setShowDebug] = useState(false);
const [isPremiumExport, setIsPremiumExport] = useState(false);
const [estimatedTimeRemaining, setEstimatedTimeRemaining] = useState(null);
const [status, setStatus] = useState('start'); // 'start', 'processing', 'complete'
+ const [downloadState, setDownloadState] = useState('idle'); // 'idle', 'downloading', 'downloaded'
const exportStartTime = useRef(null);
const lastProgressTime = useRef(null);
const lastProgress = useRef(0);
-
+
const { premiumExportRequest, premiumExportComplete } = useUserStore();
+ const handleShareOrDownload = async () => {
+ if (!videoBlob || !videoBlobFilename) {
+ console.error('No video blob available for sharing/download');
+ return;
+ }
+
+ setDownloadState('downloading');
+
+ try {
+ // Check if mobile and supports navigator.share
+ const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
+ const canShare = isMobile && navigator.share && navigator.canShare;
+
+ if (canShare) {
+ try {
+ const files = [new File([videoBlob], videoBlobFilename, { type: 'video/mp4' })];
+ if (navigator.canShare({ files })) {
+ await navigator.share({ files });
+ } else {
+ // Fallback to download if sharing files isn't supported
+ const url = URL.createObjectURL(videoBlob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = videoBlobFilename;
+ link.click();
+ URL.revokeObjectURL(url);
+ }
+ } catch (shareError) {
+ console.log('Share failed, falling back to download:', shareError);
+ // Fallback to download
+ const url = URL.createObjectURL(videoBlob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = videoBlobFilename;
+ link.click();
+ URL.revokeObjectURL(url);
+ }
+ } else {
+ // Desktop or unsupported mobile - use download
+ const url = URL.createObjectURL(videoBlob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = videoBlobFilename;
+ link.click();
+ URL.revokeObjectURL(url);
+ }
+
+ // Set to downloaded state
+ setDownloadState('downloaded');
+
+ // Reset to idle after 1.5 seconds
+ setTimeout(() => {
+ setDownloadState('idle');
+ }, 1500);
+ } catch (error) {
+ console.error('Download/share failed:', error);
+ setDownloadState('idle');
+ }
+ };
+
const handleExportWithoutWatermark = async () => {
setIsPremiumExport(true);
setEstimatedTimeRemaining(null);
-
+
// Call premiumExportRequest and check response
const response = await premiumExportRequest();
-
+
if (response?.error) {
// Halt the process if there's an error
setIsPremiumExport(false);
return;
}
-
+
if (response?.success) {
// Continue with export if successful
setStatus('processing');
@@ -227,11 +291,7 @@ const VideoDownloadModal = ({
- {isPremiumExport ? (
-
- ) : (
-
- )}
+ {isPremiumExport ? : }
Exporting {isPremiumExport ? 'without watermark' : ''}
@@ -273,9 +333,36 @@ const VideoDownloadModal = ({
Export Complete!
Your video has been successfully exported.
-
+
+
+
+
)}
diff --git a/resources/js/modules/editor/partials/canvas/video-editor.jsx b/resources/js/modules/editor/partials/canvas/video-editor.jsx
index 1241606..ffe7ee2 100644
--- a/resources/js/modules/editor/partials/canvas/video-editor.jsx
+++ b/resources/js/modules/editor/partials/canvas/video-editor.jsx
@@ -216,7 +216,7 @@ const VideoEditor = ({ width, height, onOpenTextSidebar }) => {
const totalDuration = Math.max(...timelineElements.map((el) => el.startTime + el.duration));
- const { isExporting, exportProgress, exportStatus, ffmpegCommand, copyFFmpegCommand, exportVideo } = useVideoExport({
+ const { isExporting, exportProgress, exportStatus, ffmpegCommand, copyFFmpegCommand, exportVideo, videoBlob, videoBlobFilename } = useVideoExport({
timelineElements,
dimensions,
totalDuration,
@@ -620,6 +620,8 @@ const VideoEditor = ({ width, height, onOpenTextSidebar }) => {
isExporting={isExporting}
exportProgress={exportProgress}
exportStatus={exportStatus}
+ videoBlob={videoBlob}
+ videoBlobFilename={videoBlobFilename}
/>
>
);
diff --git a/resources/js/modules/editor/partials/canvas/video-export.jsx b/resources/js/modules/editor/partials/canvas/video-export.jsx
index faf5f16..0744c15 100644
--- a/resources/js/modules/editor/partials/canvas/video-export.jsx
+++ b/resources/js/modules/editor/partials/canvas/video-export.jsx
@@ -13,6 +13,8 @@ const useVideoExport = ({ timelineElements, dimensions, totalDuration, watermark
const [isExporting, setIsExporting] = useState(false);
const [exportProgress, setExportProgress] = useState(0);
const [exportStatus, setExportStatus] = useState('');
+ const [videoBlob, setVideoBlob] = useState(null);
+ const [videoBlobFilename, setVideoBlobFilename] = useState(null);
useEffect(() => {
console.log(JSON.stringify(timelineElements));
@@ -692,43 +694,9 @@ const useVideoExport = ({ timelineElements, dimensions, totalDuration, watermark
const epochTimestamp = Date.now();
const fileName = `memeaigen-${epochTimestamp}.mp4`;
- // Check if mobile and supports navigator.share
- const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
- const canShare = isMobile && navigator.share && navigator.canShare;
-
- if (canShare) {
- try {
- const files = [new File([blob], fileName, { type: "video/mp4" })];
- if (navigator.canShare({ files })) {
- await navigator.share({ files });
- } else {
- // Fallback to download if sharing files isn't supported
- const url = URL.createObjectURL(blob);
- const link = document.createElement('a');
- link.href = url;
- link.download = fileName;
- link.click();
- URL.revokeObjectURL(url);
- }
- } catch (shareError) {
- console.log('Share failed, falling back to download:', shareError);
- // Fallback to download
- const url = URL.createObjectURL(blob);
- const link = document.createElement('a');
- link.href = url;
- link.download = fileName;
- link.click();
- URL.revokeObjectURL(url);
- }
- } else {
- // Desktop or unsupported mobile - use download
- const url = URL.createObjectURL(blob);
- const link = document.createElement('a');
- link.href = url;
- link.download = fileName;
- link.click();
- URL.revokeObjectURL(url);
- }
+ // Store the blob and filename in state instead of auto-downloading
+ setVideoBlob(blob);
+ setVideoBlobFilename(fileName);
setExportProgress(100);
setExportStatus('Complete!');
@@ -763,6 +731,8 @@ const useVideoExport = ({ timelineElements, dimensions, totalDuration, watermark
exportProgress,
exportStatus,
ffmpegCommand,
+ videoBlob,
+ videoBlobFilename,
// Functions
copyFFmpegCommand,