diff --git a/src/screens/Production/PlaybackDownload.js b/src/screens/Production/PlaybackDownload.js index 48dc8ca..0e5003d 100644 --- a/src/screens/Production/PlaybackDownload.js +++ b/src/screens/Production/PlaybackDownload.js @@ -1,17 +1,15 @@ -import React, { useEffect, useMemo, useRef, useState } from 'react' -import { Image, Platform, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native' +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { 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' import MusicLandHeader from '../../components/MusicLandHeader' import AppCheckbox from '../../components/AppCheckbox' -import firebase, { projectsRef, serverTimestamp, videosRef } from '../../config/firebase' +import firebase, { projectsRef, serverTimestamp } from '../../config/firebase' import { uploadFileToFirebase } from '../../helpers/uploadToFirebase' -import useDataFromRef from '../../hooks/useDataFromRef' import Page from '../../layouts/Page' import { Routes } from '../../navigation' import { goBack, navigate } from '../../navigation/NavigationService' @@ -137,8 +135,9 @@ const uploadSourceRecording = async ({ uri, uid, projectId }) => { const PlaybackDownload = ({ route }) => { const { currentUID } = useUserData() - const { hasActiveSubscription } = useUser() || {} - const { action, uri, project } = route.params || {} + const { hasActiveSubscription, videos } = useUser() || {} + const { action: routeAction, uri, project } = route.params || {} + const action = routeAction || 'playback' console.log('[PlaybackDownload] route params', { action, projectId: project?.id, @@ -150,18 +149,14 @@ const PlaybackDownload = ({ route }) => { const [showConfirmModal, setShowConfirmModal] = useState(false) const [pendingPlaybackUrl, setPendingPlaybackUrl] = useState(project?.playbackUrl || null) const [isPublishing, setIsPublishing] = useState(false) + const [isDownloading, setIsDownloading] = useState(false) const [hasAcceptedPublication, setHasAcceptedPublication] = useState(true) const hasShownAfterPlaybackRef = useRef(false) - const { data: video } = useDataFromRef({ - ref: videosRef.doc('fr'), - simpleRef: true, - }) - const afterPlaybackUrl = useMemo(() => { - if (!video) return null - return video?.afterPlayback - }, [video]) + if (!videos) return null + return videos?.afterPlayback || null + }, [videos]) useEffect(() => { if (action !== 'playback') return @@ -179,100 +174,152 @@ const PlaybackDownload = ({ route }) => { + const resolveAudioUrl = useCallback(() => { + if (typeof project?.songUrl === 'string' && project.songUrl.trim()) { + return project.songUrl.trim() + } + const urls = Array.isArray(project?.musicUrls) ? project.musicUrls : [] + if (!urls.length) return null + const idx = Number.isFinite(Number(project?.songIndex)) ? Number(project.songIndex) : 0 + const candidate = urls[idx] + if (typeof candidate === 'string' && candidate.trim()) { + return candidate.trim() + } + return null + }, [project]) + const handleDownloadUri = async () => { - if (isPublishing) return - if (action === 'playback' && project?.id) { - if (pendingPlaybackUrl) { - await triggerDownload(pendingPlaybackUrl, project?.title) - return - } - // Publication du playback - console.log('[PlaybackDownload] handleDownloadUri playback', { - projectId: project.id, - uri, - currentUID, + if (isPublishing || isDownloading) return + if (action !== 'playback') { + setTooltip({ + type: 'error', + text: 'Téléchargement indisponible pour cette action', }) - const audioUrl = project?.songUrl || null - if (!audioUrl) { - setTooltip({ - type: 'error', - text: 'Aucune piste audio disponible pour ce projet', - }) - return - } - let tempSourcePath = null + return + } + + const playbackUrlToDownload = pendingPlaybackUrl || project?.playbackUrl || null + if (playbackUrlToDownload) { + setIsDownloading(true) try { - setIsLoading(true) - const { sourcePath, videoUrl } = await uploadSourceRecording({ - uri, - uid: currentUID, - projectId: project.id, - }) - tempSourcePath = sourcePath - - const callable = firebase.functions().httpsCallable('upload-mergeVideoAndAudio') - - const payload = { - projectId: project?.id, - videoUrl, - audioUrl, - storagePath: `users/${currentUID}/projects/${project.id}/playback.mp4`, - } - - console.log('[PlaybackDownload] calling upload-mergeVideoAndAudio', payload) - - const { data: result } = await callable(payload) - - console.log('[PlaybackDownload] upload-mergeVideoAndAudio result', result) - - const resultURI = result?.url || null - - if (resultURI) { - setPendingPlaybackUrl(resultURI) - setTooltip({ - type: 'success', - text: 'Playback prêt à télécharger', - }) - await triggerDownload(resultURI, project?.title) - if (tempSourcePath) { - try { - await firebase.storage().ref(tempSourcePath).delete() - } catch (cleanupError) { - console.log('[PlaybackDownload] unable to delete temp source', { - message: cleanupError?.message, - code: cleanupError?.code, - }) - } - } - } else { - setTooltip({ - type: 'error', - text: 'Erreur lors de la publication du playback', - }) - } - } catch (error) { - console.log('[PlaybackDownload] error upload playback', { - message: error?.message, - code: error?.code, - name: error?.name, - details: error?.details, + await triggerDownload(playbackUrlToDownload, project?.title) + } finally { + setIsDownloading(false) + } + return + } + + if (!project?.id) { + setTooltip({ + type: 'error', + text: 'Projet introuvable pour ce playback', + }) + return + } + + if (!uri) { + setTooltip({ + type: 'error', + text: 'Aucune vidéo trouvée pour ce playback', + }) + return + } + + if (!currentUID) { + setTooltip({ + type: 'error', + text: 'Utilisateur non authentifié', + }) + return + } + + const audioUrl = resolveAudioUrl() + if (!audioUrl) { + setTooltip({ + type: 'error', + text: 'Aucune piste audio disponible pour ce projet', + }) + return + } + + // Publication du playback + console.log('[PlaybackDownload] handleDownloadUri playback', { + projectId: project.id, + uri, + currentUID, + }) + + let tempSourcePath = null + try { + setIsDownloading(true) + setIsLoading(true) + const { sourcePath, videoUrl } = await uploadSourceRecording({ + uri, + uid: currentUID, + projectId: project.id, + }) + tempSourcePath = sourcePath + + const callable = firebase.functions().httpsCallable('upload-mergeVideoAndAudio') + + const payload = { + projectId: project?.id, + videoUrl, + audioUrl, + storagePath: `users/${currentUID}/projects/${project.id}/playback.mp4`, + } + + console.log('[PlaybackDownload] calling upload-mergeVideoAndAudio', payload) + + const { data: result } = await callable(payload) + + console.log('[PlaybackDownload] upload-mergeVideoAndAudio result', result) + + const resultURI = result?.url || null + + if (resultURI) { + setPendingPlaybackUrl(resultURI) + setTooltip({ + type: 'success', + text: 'Playback prêt à télécharger', }) + await triggerDownload(resultURI, project?.title) if (tempSourcePath) { try { await firebase.storage().ref(tempSourcePath).delete() - } catch { } + } catch (cleanupError) { + console.log('[PlaybackDownload] unable to delete temp source', { + message: cleanupError?.message, + code: cleanupError?.code, + }) + } } + } else { setTooltip({ type: 'error', - text: String(error?.message || 'Erreur lors de la publication du playback'), + text: 'Erreur lors de la publication du playback', }) - } finally { - releaseBlobUrl(uri || null) - setIsLoading(false) } - // Handle playback download - } else { - // Handle song download + } catch (error) { + console.log('[PlaybackDownload] error upload playback', { + message: error?.message, + code: error?.code, + name: error?.name, + details: error?.details, + }) + if (tempSourcePath) { + try { + await firebase.storage().ref(tempSourcePath).delete() + } catch { } + } + setTooltip({ + type: 'error', + text: String(error?.message || 'Erreur lors de la publication du playback'), + }) + } finally { + releaseBlobUrl(uri || null) + setIsDownloading(false) + setIsLoading(false) } } @@ -299,7 +346,7 @@ const PlaybackDownload = ({ route }) => { } let tempSourcePath = null let playbackUrlToSave = pendingPlaybackUrl || project?.playbackUrl || null - const audioUrl = project?.songUrl || null + const audioUrl = resolveAudioUrl() if (!audioUrl && !playbackUrlToSave) { setTooltip({ type: 'error', @@ -307,6 +354,13 @@ const PlaybackDownload = ({ route }) => { }) return } + if (!currentUID) { + setTooltip({ + type: 'error', + text: 'Utilisateur non authentifié', + }) + return + } try { setIsPublishing(true) @@ -417,7 +471,11 @@ const PlaybackDownload = ({ route }) => { )} - + Télécharger le playback