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 { Image, Platform, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'
|
||||||
import { MaterialCommunityIcons } from '@expo/vector-icons'
|
import { MaterialCommunityIcons } from '@expo/vector-icons'
|
||||||
import useMinuit from 'react-native-minuit/src/hooks/useMinuit.js'
|
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 { background } from '../../assets'
|
||||||
import FullscreenIntroVideo from '../../components/FullscreenIntroVideo'
|
import FullscreenIntroVideo from '../../components/FullscreenIntroVideo'
|
||||||
import BorderGradientButton from '../../components/BorderGradientButton'
|
import BorderGradientButton from '../../components/BorderGradientButton'
|
||||||
@@ -24,14 +26,16 @@ import { Image as ExpoImage } from 'expo-image'
|
|||||||
import SubscriptionConfirmModal from '../../components/SubscriptionConfirmModal'
|
import SubscriptionConfirmModal from '../../components/SubscriptionConfirmModal'
|
||||||
import { isWeb } from '../../hooks/useLayoutType'
|
import { isWeb } from '../../hooks/useLayoutType'
|
||||||
|
|
||||||
const triggerWebDownload = async (url, title) => {
|
const triggerDownload = async (url, title) => {
|
||||||
if (Platform.OS !== 'web') return
|
|
||||||
if (!url) return
|
if (!url) return
|
||||||
if (typeof document === 'undefined') return
|
|
||||||
const baseName = (title || 'Playback').toString().trim() || 'Playback'
|
const baseName = (title || 'Playback').toString().trim() || 'Playback'
|
||||||
const sanitized = baseName.replace(/[\\/:*?"<>|]/g, '-')
|
const sanitized = baseName.replace(/[\\/:*?"<>|]/g, '-')
|
||||||
const extension = guessExtension(url) || 'mp4'
|
const extension = guessExtension(url) || 'mp4'
|
||||||
const filename = `${sanitized}.${extension}`
|
const filename = `${sanitized}.${extension}`
|
||||||
|
|
||||||
|
// WEB
|
||||||
|
if (Platform.OS === 'web') {
|
||||||
|
if (typeof document === 'undefined') return
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
if (!response.ok || response.type === 'opaque') {
|
if (!response.ok || response.type === 'opaque') {
|
||||||
@@ -46,11 +50,13 @@ const triggerWebDownload = async (url, title) => {
|
|||||||
document.body.appendChild(anchor)
|
document.body.appendChild(anchor)
|
||||||
anchor.click()
|
anchor.click()
|
||||||
document.body.removeChild(anchor)
|
document.body.removeChild(anchor)
|
||||||
URL.revokeObjectURL(blobUrl)
|
// Revoke with delay to ensure browser captures it
|
||||||
|
setTimeout(() => URL.revokeObjectURL(blobUrl), 150)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('[PlaybackDownload] web download fallback', {
|
console.log('[PlaybackDownload] web download fallback', {
|
||||||
message: error?.message,
|
message: error?.message,
|
||||||
})
|
})
|
||||||
|
// First fallback: Anchor with _blank
|
||||||
try {
|
try {
|
||||||
const anchor = document.createElement('a')
|
const anchor = document.createElement('a')
|
||||||
anchor.href = url
|
anchor.href = url
|
||||||
@@ -60,17 +66,32 @@ const triggerWebDownload = async (url, title) => {
|
|||||||
document.body.appendChild(anchor)
|
document.body.appendChild(anchor)
|
||||||
anchor.click()
|
anchor.click()
|
||||||
document.body.removeChild(anchor)
|
document.body.removeChild(anchor)
|
||||||
return
|
|
||||||
} catch (fallbackError) {
|
} catch (fallbackError) {
|
||||||
console.log('[PlaybackDownload] anchor fallback failed', {
|
console.log('[PlaybackDownload] anchor fallback failed', {
|
||||||
message: fallbackError?.message,
|
message: fallbackError?.message,
|
||||||
})
|
})
|
||||||
}
|
// Ultimate fallback: direct window open
|
||||||
try {
|
try {
|
||||||
window.open(url, '_blank', 'noopener,noreferrer')
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const guessExtension = (inputUri = '') => {
|
const guessExtension = (inputUri = '') => {
|
||||||
const cleaned = inputUri.split('?')[0] || ''
|
const cleaned = inputUri.split('?')[0] || ''
|
||||||
@@ -156,17 +177,13 @@ const PlaybackDownload = ({ route }) => {
|
|||||||
}
|
}
|
||||||
}, [project?.playbackUrl])
|
}, [project?.playbackUrl])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
releaseBlobUrl(uri || null)
|
|
||||||
}
|
|
||||||
}, [uri])
|
|
||||||
|
|
||||||
const handleDownloadUri = async () => {
|
const handleDownloadUri = async () => {
|
||||||
if (isPublishing) return
|
if (isPublishing) return
|
||||||
if (action === 'playback' && project?.id) {
|
if (action === 'playback' && project?.id) {
|
||||||
if (pendingPlaybackUrl) {
|
if (pendingPlaybackUrl) {
|
||||||
await triggerWebDownload(pendingPlaybackUrl, project?.title)
|
await triggerDownload(pendingPlaybackUrl, project?.title)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Publication du playback
|
// Publication du playback
|
||||||
@@ -216,7 +233,7 @@ const PlaybackDownload = ({ route }) => {
|
|||||||
type: 'success',
|
type: 'success',
|
||||||
text: 'Playback prêt à télécharger',
|
text: 'Playback prêt à télécharger',
|
||||||
})
|
})
|
||||||
await triggerWebDownload(resultURI, project?.title)
|
await triggerDownload(resultURI, project?.title)
|
||||||
if (tempSourcePath) {
|
if (tempSourcePath) {
|
||||||
try {
|
try {
|
||||||
await firebase.storage().ref(tempSourcePath).delete()
|
await firebase.storage().ref(tempSourcePath).delete()
|
||||||
@@ -369,6 +386,11 @@ const PlaybackDownload = ({ route }) => {
|
|||||||
const continueLabel = hasActiveSubscription ? 'Publier' : 'Publier sans générer de revenus'
|
const continueLabel = hasActiveSubscription ? 'Publier' : 'Publier sans générer de revenus'
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<FullscreenIntroVideo
|
||||||
|
visible={isAfterPlaybackVideoVisible}
|
||||||
|
url={afterPlaybackUrl}
|
||||||
|
onClose={() => setIsAfterPlaybackVideoVisible(false)}
|
||||||
|
/>
|
||||||
<Page
|
<Page
|
||||||
// backgroundImg={
|
// backgroundImg={
|
||||||
// action === "playback"
|
// action === "playback"
|
||||||
|
|||||||
Reference in New Issue
Block a user