feat: download playback mp4
This commit is contained in:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user