Skip to content

Instantly share code, notes, and snippets.

@digitallysavvy
Created September 24, 2025 14:04
Show Gist options
  • Save digitallysavvy/f799ba36412f557c7a5533d31bdea4df to your computer and use it in GitHub Desktop.
Save digitallysavvy/f799ba36412f557c7a5533d31bdea4df to your computer and use it in GitHub Desktop.
Download Audio-Video as blob on iOS mobile.
// Save the captured media
function saveMedia() {
console.log('Saving media', { mediaType });
if (!capturedMedia) {
console.error('No media to save');
return;
}
try {
// Check if this is iOS
const isIOS =
/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
if (mediaType === 'photo') {
// Convert data URL to blob for sharing
const fetchResponse = fetch(capturedMedia);
fetchResponse
.then((res) => res.blob())
.then((blob) => {
const timestamp = new Date().getTime();
const photoFile = new File([blob], `photo-${timestamp}.jpg`, {
type: 'image/jpeg',
});
if (navigator.share && navigator.canShare({ files: [photoFile] })) {
// Use Web Share API
navigator
.share({
title: 'Photo',
files: [photoFile],
})
.then(() => {
console.log('Photo shared successfully');
closePreview();
})
.catch((error) => {
console.error('Error sharing photo:', error);
// Do not close the preview if sharing is canceled or fails
// This allows the user to try again or choose to close
console.log('Keeping preview open to allow retry');
});
} else {
// If Web Share API not available, just keep the preview open
console.log('Share API not available, keeping preview open');
// No fallback to download - let user use preview controls
}
})
.catch((error) => {
console.error('Error preparing photo for sharing:', error);
// Keep preview open if there's an error preparing the photo
});
} else if (mediaType === 'video') {
if (isIOS) {
// On iOS, we need to handle video specially
// Store a reference to the video blob before closing preview
const videoBlob = capturedMedia;
// Create a temporary URL
const videoUrl = URL.createObjectURL(videoBlob);
const timestamp = new Date().getTime();
// Simply share the video instead of showing a custom overlay
// This works better on iOS and avoids pipeline issues
try {
if (navigator.share) {
// Use Web Share API if available (iOS 12+)
const shareData = {
title: 'Video',
files: [
new File([videoBlob], `video-${timestamp}.mp4`, {
type: videoBlob.type,
}),
],
};
// Share the video but don't close preview until sharing is confirmed successful
navigator
.share(shareData)
.then(() => {
console.log('Video shared successfully');
closePreview();
})
.catch((error) => {
console.error('Error sharing video:', error);
// Don't close preview or show fallback UI if sharing is canceled
// This allows the user to try again or choose to close
console.log('Keeping preview open to allow retry');
// Revoke the temporary URL to avoid memory leaks
URL.revokeObjectURL(videoUrl);
});
} else {
// Show fallback UI for older iOS
showVideoFallbackUI(videoUrl);
}
} catch (shareError) {
console.error('Error with share functionality:', shareError);
showVideoFallbackUI(videoUrl);
}
} else {
// Non-iOS devices can use the download attribute
const a = document.createElement('a');
a.href = URL.createObjectURL(capturedMedia);
a.download = `video-${new Date().getTime()}.webm`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
// Cleanup the object URL
URL.revokeObjectURL(a.href);
// Close the preview after saving
closePreview();
}
}
} catch (error) {
console.error('Error saving media:', error);
// If there's an error, still try to close the preview
// to get back to a working state
closePreview();
// Notify the user
alert('There was an error saving your media. Please try again.');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment