feat: download playback mp4

This commit is contained in:
2026-01-16 15:25:14 +01:00
parent f947cd194f
commit 9581a6fd4c
+37 -15
View File
@@ -2,6 +2,8 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'
import { Image, Platform, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'
import { MaterialCommunityIcons } from '@expo/vector-icons'
import useMinuit from 'react-native-minuit/src/hooks/useMinuit.js'
import * as FileSystem from 'expo-file-system'
import { shareAsync } from 'expo-sharing'
import { background } from '../../assets'
import FullscreenIntroVideo from '../../components/FullscreenIntroVideo'
import BorderGradientButton from '../../components/BorderGradientButton'
@@ -24,14 +26,16 @@ import { Image as ExpoImage } from 'expo-image'
import SubscriptionConfirmModal from '../../components/SubscriptionConfirmModal'
import { isWeb } from '../../hooks/useLayoutType'
const triggerWebDownload = async (url, title) => {
if (Platform.OS !== 'web') return
const triggerDownload = async (url, title) => {
if (!url) return
if (typeof document === 'undefined') return
const baseName = (title || 'Playback').toString().trim() || 'Playback'
const sanitized = baseName.replace(/[\\/:*?"<>|]/g, '-')
const extension = guessExtension(url) || 'mp4'
const filename = `${sanitized}.${extension}`
// WEB
if (Platform.OS === 'web') {
if (typeof document === 'undefined') return
try {
const response = await fetch(url)
if (!response.ok || response.type === 'opaque') {
@@ -46,11 +50,13 @@ const triggerWebDownload = async (url, title) => {
document.body.appendChild(anchor)
anchor.click()
document.body.removeChild(anchor)
URL.revokeObjectURL(blobUrl)
// Revoke with delay to ensure browser captures it
setTimeout(() => URL.revokeObjectURL(blobUrl), 150)
} catch (error) {
console.log('[PlaybackDownload] web download fallback', {
message: error?.message,
})
// First fallback: Anchor with _blank
try {
const anchor = document.createElement('a')
anchor.href = url
@@ -60,15 +66,30 @@ const triggerWebDownload = async (url, title) => {
document.body.appendChild(anchor)
anchor.click()
document.body.removeChild(anchor)
return
} catch (fallbackError) {
console.log('[PlaybackDownload] anchor fallback failed', {
message: fallbackError?.message,
})
}
// Ultimate fallback: direct window open
try {
window.open(url, '_blank', 'noopener,noreferrer')
} catch {}
} catch { }
}
}
} else {
// NATIVE (Android / iOS)
try {
if (FileSystem?.downloadAsync) {
const fileUri = FileSystem.documentDirectory + filename
const { uri: localUri } = await FileSystem.downloadAsync(url, fileUri)
console.log('Finished downloading to ', localUri)
if (shareAsync) {
await shareAsync(localUri)
}
}
} catch (e) {
console.error(e)
}
}
}
@@ -156,17 +177,13 @@ const PlaybackDownload = ({ route }) => {
}
}, [project?.playbackUrl])
useEffect(() => {
return () => {
releaseBlobUrl(uri || null)
}
}, [uri])
const handleDownloadUri = async () => {
if (isPublishing) return
if (action === 'playback' && project?.id) {
if (pendingPlaybackUrl) {
await triggerWebDownload(pendingPlaybackUrl, project?.title)
await triggerDownload(pendingPlaybackUrl, project?.title)
return
}
// Publication du playback
@@ -216,7 +233,7 @@ const PlaybackDownload = ({ route }) => {
type: 'success',
text: 'Playback prêt à télécharger',
})
await triggerWebDownload(resultURI, project?.title)
await triggerDownload(resultURI, project?.title)
if (tempSourcePath) {
try {
await firebase.storage().ref(tempSourcePath).delete()
@@ -243,7 +260,7 @@ const PlaybackDownload = ({ route }) => {
if (tempSourcePath) {
try {
await firebase.storage().ref(tempSourcePath).delete()
} catch {}
} catch { }
}
setTooltip({
type: 'error',
@@ -369,6 +386,11 @@ const PlaybackDownload = ({ route }) => {
const continueLabel = hasActiveSubscription ? 'Publier' : 'Publier sans générer de revenus'
return (
<>
<FullscreenIntroVideo
visible={isAfterPlaybackVideoVisible}
url={afterPlaybackUrl}
onClose={() => setIsAfterPlaybackVideoVisible(false)}
/>
<Page
// backgroundImg={
// action === "playback"