Skip to content

Instantly share code, notes, and snippets.

@ayoubzulfiqar
Created July 31, 2025 10:12
Show Gist options
  • Save ayoubzulfiqar/23102d41ac3cd6baf8acf571a2d15f12 to your computer and use it in GitHub Desktop.
Save ayoubzulfiqar/23102d41ac3cd6baf8acf571a2d15f12 to your computer and use it in GitHub Desktop.
Dirct Download Button

Solution 1: Fetch + Blob (Optimized for Streaming)

const handleDownloadClick = useCallback(async (url: string, formatExt: VideoFormat) => {
    const title = videoInfo.title;
    const filename = `${title.replace(/[^a-z0-9\-_ ]/gi, '').replace(/\s+/g, '_') || 'download'}.${formatExt.ext}`;

    try {
        console.log('Starting download for:', filename);
        
        // Show loading indicator
        const loadingElement = document.createElement('div');
        loadingElement.textContent = 'Preparing download...';
        loadingElement.style.cssText = `
            position: fixed; top: 20px; right: 20px; 
            background: #333; color: white; padding: 10px;
            border-radius: 4px; z-index: 9999;
        `;
        document.body.appendChild(loadingElement);

        // Fetch with streaming
        const response = await fetch(url);
        if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        
        // Get content length for progress (if available)
        const contentLength = response.headers.get('content-length');
        const total = parseInt(contentLength, 10);
        
        // Create readable stream
        const reader = response.body.getReader();
        const chunks = [];
        let receivedLength = 0;

        // Read chunks progressively
        while (true) {
            const { done, value } = await reader.read();
            
            if (done) break;
            
            chunks.push(value);
            receivedLength += value.length;
            
            // Update progress if needed
            if (total) {
                const percent = Math.round((receivedLength / total) * 100);
                loadingElement.textContent = `Downloading... ${percent}%`;
            }
        }

        // Combine chunks into blob
        const blob = new Blob(chunks);
        
        // Create download link
        const blobUrl = window.URL.createObjectURL(blob);
        const link = document.createElement('a');
        link.href = blobUrl;
        link.download = filename;
        document.body.appendChild(link);
        link.click();
        
        // Cleanup
        setTimeout(() => {
            document.body.removeChild(link);
            window.URL.revokeObjectURL(blobUrl);
            if (loadingElement.parentNode) {
                document.body.removeChild(loadingElement);
            }
        }, 1000);

    } catch (error) {
        console.error('Download failed:', error);
        alert('Download failed. Please try again or copy the URL manually.');
    }
}, [videoInfo.title]);

Solution 2: Simple Fetch + Blob (Faster)

const handleDownloadClick = useCallback(async (url: string, formatExt: VideoFormat) => {
    const title = videoInfo.title;
    const filename = `${title.replace(/[^a-z0-9\-_ ]/gi, '').replace(/\s+/g, '_') || 'download'}.${formatExt.ext}`;

    try {
        // Simple fetch approach
        const response = await fetch(url, {
            method: 'GET',
            headers: {
                // Add headers that might help
                'Accept': '*/*',
                'User-Agent': navigator.userAgent
            }
        });

        if (!response.ok) {
            throw new Error(`Failed to fetch: ${response.status}`);
        }

        const blob = await response.blob();
        const blobUrl = window.URL.createObjectURL(blob);
        
        const link = document.createElement('a');
        link.href = blobUrl;
        link.download = filename;
        document.body.appendChild(link);
        link.click();
        
        // Cleanup
        setTimeout(() => {
            document.body.removeChild(link);
            window.URL.revokeObjectURL(blobUrl);
        }, 100);

    } catch (error) {
        console.error('Download failed:', error);
        // Fallback to simple window.open
        window.open(url, '_blank');
    }
}, [videoInfo.title]);

Solution 3: XMLHttpRequest Approach (Most Compatible)

const handleDownloadClick = useCallback((url: string, formatExt: VideoFormat) => {
    const title = videoInfo.title;
    const filename = `${title.replace(/[^a-z0-9\-_ ]/gi, '').replace(/\s+/g, '_') || 'download'}.${formatExt.ext}`;

    const xhr = new XMLHttpRequest();
    
    // Show progress
    const progressDiv = document.createElement('div');
    progressDiv.style.cssText = `
        position: fixed; top: 20px; right: 20px; 
        background: #333; color: white; padding: 10px;
        border-radius: 4px; z-index: 9999;
    `;
    progressDiv.textContent = 'Starting download...';
    document.body.appendChild(progressDiv);

    xhr.open('GET', url, true);
    xhr.responseType = 'blob';

    xhr.onprogress = function(event) {
        if (event.lengthComputable) {
            const percentComplete = (event.loaded / event.total) * 100;
            progressDiv.textContent = `Downloading... ${Math.round(percentComplete)}%`;
        }
    };

    xhr.onload = function() {
        if (xhr.status === 200) {
            const blob = xhr.response;
            const blobUrl = window.URL.createObjectURL(blob);
            
            const link = document.createElement('a');
            link.href = blobUrl;
            link.download = filename;
            document.body.appendChild(link);
            link.click();
            
            // Cleanup
            setTimeout(() => {
                document.body.removeChild(link);
                window.URL.revokeObjectURL(blobUrl);
                if (progressDiv.parentNode) {
                    document.body.removeChild(progressDiv);
                }
            }, 1000);
        } else {
            console.error('Download failed with status:', xhr.status);
            if (progressDiv.parentNode) {
                document.body.removeChild(progressDiv);
            }
            alert('Download failed. Opening in new tab instead.');
            window.open(url, '_blank');
        }
    };

    xhr.onerror = function() {
        console.error('Network error during download');
        if (progressDiv.parentNode) {
            document.body.removeChild(progressDiv);
        }
        alert('Network error. Opening in new tab instead.');
        window.open(url, '_blank');
    };

    xhr.send();
}, [videoInfo.title]);

Solution 4: Server-Side Proxy (Recommended for Facebook)

Since Facebook videos work with curl but not directly in browser, create a simple proxy endpoint:

// Server-side endpoint (Express.js example)
app.get('/api/proxy-download', async (req, res) => {
    const { url, filename } = req.query;
    
    try {
        const response = await fetch(decodeURIComponent(url));
        
        if (!response.ok) {
            return res.status(response.status).send('Failed to fetch video');
        }
        
        // Set headers to force download
        res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
        res.setHeader('Content-Type', response.headers.get('content-type') || 'application/octet-stream');
        
        // Stream the response
        response.body.pipe(res);
    } catch (error) {
        res.status(500).send('Download failed');
    }
});

Then use it in your frontend:

const handleDownloadClick = useCallback((url: string, formatExt: VideoFormat) => {
    const title = videoInfo.title;
    const filename = `${title.replace(/[^a-z0-9\-_ ]/gi, '').replace(/\s+/g, '_') || 'download'}.${formatExt.ext}`;

    // Use your proxy endpoint
    const proxyUrl = `/api/proxy-download?url=${encodeURIComponent(url)}&filename=${encodeURIComponent(filename)}`;
    
    window.location.href = proxyUrl;
}, [videoInfo.title]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment