global player
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useAudioPlayer } from "expo-audio";
|
||||
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
|
||||
import { Image as ExpoImage } from "expo-image";
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import {
|
||||
@@ -75,7 +75,14 @@ const MusicDetails = ({ route }) => {
|
||||
const coverUrl = project?.coverUrl || null;
|
||||
const songUrl = project?.songUrl || null;
|
||||
|
||||
const player = useAudioPlayer(songUrl ? { uri: songUrl } : undefined);
|
||||
const player = useSharedAudioPlayer(songUrl ? { uri: songUrl } : undefined, {
|
||||
id: projectId ? `project-${projectId}` : songUrl ? `song-${songUrl}` : undefined,
|
||||
title,
|
||||
artist,
|
||||
artwork: coverUrl,
|
||||
coverUrl,
|
||||
metadata: { projectId, screen: "MusicDetails" },
|
||||
});
|
||||
|
||||
// Karaoke aligned words (from timestamps)
|
||||
const alignedWords = useMemo(() => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BlurView } from "expo-blur";
|
||||
import { Image as ExpoImage } from "expo-image";
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import {
|
||||
Pressable,
|
||||
Image as RNImage,
|
||||
@@ -26,119 +26,12 @@ import {
|
||||
usersRef,
|
||||
} from "../../config/firebase";
|
||||
import useDataFromRef from "../../hooks/useDataFromRef";
|
||||
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
|
||||
import Page from "../../layouts/Page";
|
||||
import { Palette } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import Style, { gutters, size } from "../../styles/Style";
|
||||
|
||||
const useHtmlAudioPlayer = (source) => {
|
||||
const src = typeof source === "string" ? source : source?.uri || null;
|
||||
const audioRef = useRef(null);
|
||||
const [state, setState] = useState({
|
||||
durationMs: 0,
|
||||
positionMs: 0,
|
||||
isPlaying: false,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setState({ durationMs: 0, positionMs: 0, isPlaying: false });
|
||||
|
||||
if (!src) {
|
||||
if (audioRef.current) {
|
||||
audioRef.current.pause();
|
||||
audioRef.current.src = "";
|
||||
audioRef.current.load();
|
||||
audioRef.current = null;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const audio = new Audio(src);
|
||||
audio.preload = "auto";
|
||||
audio.crossOrigin = "anonymous";
|
||||
audioRef.current = audio;
|
||||
|
||||
const syncDuration = () => {
|
||||
const duration = Number.isFinite(audio.duration)
|
||||
? audio.duration * 1000
|
||||
: 0;
|
||||
setState((prev) => ({ ...prev, durationMs: duration }));
|
||||
};
|
||||
|
||||
const handleLoadedMetadata = () => syncDuration();
|
||||
const handleDurationChange = () => syncDuration();
|
||||
const handleTimeUpdate = () => {
|
||||
setState((prev) => ({
|
||||
...prev,
|
||||
positionMs: audio.currentTime * 1000,
|
||||
}));
|
||||
};
|
||||
const handlePlay = () => setState((prev) => ({ ...prev, isPlaying: true }));
|
||||
const handlePause = () =>
|
||||
setState((prev) => ({ ...prev, isPlaying: false }));
|
||||
const handleEnded = () =>
|
||||
setState((prev) => ({
|
||||
...prev,
|
||||
positionMs: Number.isFinite(audio.duration)
|
||||
? audio.duration * 1000
|
||||
: prev.positionMs,
|
||||
isPlaying: false,
|
||||
}));
|
||||
|
||||
audio.addEventListener("loadedmetadata", handleLoadedMetadata);
|
||||
audio.addEventListener("durationchange", handleDurationChange);
|
||||
audio.addEventListener("timeupdate", handleTimeUpdate);
|
||||
audio.addEventListener("play", handlePlay);
|
||||
audio.addEventListener("pause", handlePause);
|
||||
audio.addEventListener("ended", handleEnded);
|
||||
|
||||
return () => {
|
||||
audio.pause();
|
||||
audio.removeEventListener("loadedmetadata", handleLoadedMetadata);
|
||||
audio.removeEventListener("durationchange", handleDurationChange);
|
||||
audio.removeEventListener("timeupdate", handleTimeUpdate);
|
||||
audio.removeEventListener("play", handlePlay);
|
||||
audio.removeEventListener("pause", handlePause);
|
||||
audio.removeEventListener("ended", handleEnded);
|
||||
audio.src = "";
|
||||
audio.load();
|
||||
audioRef.current = null;
|
||||
};
|
||||
}, [src]);
|
||||
|
||||
const play = React.useCallback(async () => {
|
||||
if (!audioRef.current) return;
|
||||
try {
|
||||
await audioRef.current.play();
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const pause = React.useCallback(() => {
|
||||
if (!audioRef.current) return;
|
||||
audioRef.current.pause();
|
||||
}, []);
|
||||
|
||||
const seekTo = React.useCallback((seconds) => {
|
||||
if (!audioRef.current) return;
|
||||
const next = Math.max(0, Number(seconds) || 0);
|
||||
const duration = audioRef.current.duration;
|
||||
const bounded = Number.isFinite(duration) ? Math.min(next, duration) : next;
|
||||
audioRef.current.currentTime = bounded;
|
||||
setState((prev) => ({ ...prev, positionMs: bounded * 1000 }));
|
||||
}, []);
|
||||
|
||||
return {
|
||||
play,
|
||||
pause,
|
||||
seekTo,
|
||||
playing: state.isPlaying,
|
||||
positionMs: state.positionMs,
|
||||
durationMs: state.durationMs,
|
||||
};
|
||||
};
|
||||
|
||||
// 20 secondes
|
||||
const timeBeforeIncrement = 20000;
|
||||
|
||||
@@ -183,14 +76,29 @@ const MusicDetails = ({ route }) => {
|
||||
const coverUrl = project?.coverUrl || null;
|
||||
const songUrl = project?.songUrl || null;
|
||||
|
||||
const {
|
||||
play: playAudio,
|
||||
pause: pauseAudio,
|
||||
seekTo: seekAudio,
|
||||
playing: isPlaying,
|
||||
positionMs,
|
||||
durationMs,
|
||||
} = useHtmlAudioPlayer(songUrl ? { uri: songUrl } : undefined);
|
||||
const sharedPlayer = useSharedAudioPlayer(songUrl ? { uri: songUrl } : undefined, {
|
||||
id: projectId ? `project-${projectId}` : songUrl ? `song-${songUrl}` : undefined,
|
||||
title,
|
||||
artist,
|
||||
artwork: coverUrl,
|
||||
coverUrl,
|
||||
metadata: { projectId, screen: "MusicDetails" },
|
||||
});
|
||||
const playAudio = useCallback(async () => {
|
||||
if (sharedPlayer?.play) await sharedPlayer.play();
|
||||
}, [sharedPlayer]);
|
||||
const pauseAudio = useCallback(async () => {
|
||||
if (sharedPlayer?.pause) await sharedPlayer.pause();
|
||||
}, [sharedPlayer]);
|
||||
const seekAudio = useCallback(
|
||||
(seconds) => {
|
||||
if (sharedPlayer?.seekTo) sharedPlayer.seekTo(seconds);
|
||||
},
|
||||
[sharedPlayer]
|
||||
);
|
||||
const isPlaying = !!sharedPlayer?.playing;
|
||||
const positionMs = Math.max(0, (sharedPlayer?.currentTime || 0) * 1000);
|
||||
const durationMs = Math.max(0, (sharedPlayer?.duration || 0) * 1000);
|
||||
|
||||
// Karaoke aligned words (from timestamps)
|
||||
const alignedWords = useMemo(() => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useFocusEffect } from "@react-navigation/native";
|
||||
import { useAudioPlayer } from "expo-audio";
|
||||
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
|
||||
import { CameraView, useCameraPermissions } from "expo-camera";
|
||||
import React, {
|
||||
useCallback,
|
||||
@@ -54,7 +54,14 @@ const RecordPlayback = ({ route }) => {
|
||||
|
||||
// Musique
|
||||
const songUrl = project?.songUrl || null;
|
||||
const player = useAudioPlayer(songUrl ? { uri: songUrl } : undefined);
|
||||
const player = useSharedAudioPlayer(songUrl ? { uri: songUrl } : undefined, {
|
||||
id: projectId ? `record-${projectId}` : songUrl ? `record-${songUrl}` : undefined,
|
||||
title: typeof project?.title === "string" ? project.title : "Sans titre",
|
||||
artist: typeof project?.userName === "string" ? project.userName : "MusicLand",
|
||||
artwork: project?.coverUrl || null,
|
||||
coverUrl: project?.coverUrl || null,
|
||||
metadata: { projectId, screen: "RecordPlayback" },
|
||||
});
|
||||
|
||||
const musicIndex = useMemo(() => {
|
||||
const i = Number(songIndex);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useFocusEffect } from "@react-navigation/native";
|
||||
import { useAudioPlayer } from "expo-audio";
|
||||
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
|
||||
import { useCameraPermissions } from "expo-camera";
|
||||
import React, {
|
||||
useCallback,
|
||||
@@ -61,7 +61,14 @@ const RecordPlayback = ({ route }) => {
|
||||
|
||||
// Player
|
||||
const songUrl = project?.songUrl || null;
|
||||
const player = useAudioPlayer(songUrl ? { uri: songUrl } : undefined);
|
||||
const player = useSharedAudioPlayer(songUrl ? { uri: songUrl } : undefined, {
|
||||
id: projectId ? `record-${projectId}` : songUrl ? `record-${songUrl}` : undefined,
|
||||
title: typeof project?.title === "string" ? project.title : "Sans titre",
|
||||
artist: typeof project?.userName === "string" ? project.userName : "MusicLand",
|
||||
artwork: project?.coverUrl || null,
|
||||
coverUrl: project?.coverUrl || null,
|
||||
metadata: { projectId, screen: "RecordPlayback" },
|
||||
});
|
||||
|
||||
// MediaStream / Recorder (web only)
|
||||
const previewVideoRef = useRef(null);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useAudioPlayer } from "expo-audio";
|
||||
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
|
||||
import * as FileSystem from "expo-file-system";
|
||||
import { VideoView, useVideoPlayer } from "expo-video";
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
@@ -16,7 +16,14 @@ const RecordedPlayback = ({ route }) => {
|
||||
const { videoUri, project } = route.params || {};
|
||||
const songUrl = project?.songUrl || null;
|
||||
console.log("video uri is : ", videoUri);
|
||||
const audioPlayer = useAudioPlayer(songUrl ? { uri: songUrl } : undefined);
|
||||
const audioPlayer = useSharedAudioPlayer(songUrl ? { uri: songUrl } : undefined, {
|
||||
id: project?.id ? `recorded-${project.id}` : songUrl ? `recorded-${songUrl}` : undefined,
|
||||
title: typeof project?.title === "string" ? project.title : "Sans titre",
|
||||
artist: typeof project?.userName === "string" ? project.userName : "MusicLand",
|
||||
artwork: project?.coverUrl || null,
|
||||
coverUrl: project?.coverUrl || null,
|
||||
metadata: { projectId: project?.id, screen: "RecordedPlayback" },
|
||||
});
|
||||
const videoPlayer = useVideoPlayer(videoUri || null, (p) => {
|
||||
p.loop = false;
|
||||
p.muted = true; // recorded video has no audio; keep muted anyway
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useAudioPlayer } from "expo-audio";
|
||||
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Text, View } from "react-native";
|
||||
import { background } from "../../assets";
|
||||
@@ -29,7 +29,14 @@ const RecordedPlayback = ({ route }) => {
|
||||
const songUrl = project?.songUrl || null;
|
||||
console.log("video uri is : ", videoUri);
|
||||
// AUDIO PLAYER (expo-audio → seconds)
|
||||
const audioPlayer = useAudioPlayer(songUrl ? { uri: songUrl } : undefined);
|
||||
const audioPlayer = useSharedAudioPlayer(songUrl ? { uri: songUrl } : undefined, {
|
||||
id: project?.id ? `recorded-${project.id}` : songUrl ? `recorded-${songUrl}` : undefined,
|
||||
title: typeof project?.title === "string" ? project.title : "Sans titre",
|
||||
artist: typeof project?.userName === "string" ? project.userName : "MusicLand",
|
||||
artwork: project?.coverUrl || null,
|
||||
coverUrl: project?.coverUrl || null,
|
||||
metadata: { projectId: project?.id, screen: "RecordedPlayback" },
|
||||
});
|
||||
|
||||
// Optionnel: si tu veux quand même un rendu vidéo sur web si tu as une source
|
||||
const videoElRef = useRef(null);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useIsFocused, useRoute } from "@react-navigation/native";
|
||||
import { useAudioPlayer } from "expo-audio";
|
||||
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
|
||||
import { BlurView } from "expo-blur";
|
||||
import { VideoView, useVideoPlayer } from "expo-video";
|
||||
import React, {
|
||||
@@ -115,7 +115,14 @@ const PlaybackItem = ({ item, isActive, userCache, getUserByUid }) => {
|
||||
|
||||
const creatorName = item?.userName;
|
||||
|
||||
const audioPlayer = useAudioPlayer(audioSource || undefined);
|
||||
const audioPlayer = useSharedAudioPlayer(audioSource || undefined, {
|
||||
id: item?.id ? `playback-${item.id}` : audioSource?.uri ? `playback-${audioSource.uri}` : undefined,
|
||||
title: typeof item?.title === "string" ? item.title : "",
|
||||
artist: typeof item?.userName === "string" ? item.userName : "",
|
||||
artwork: item?.coverUrl || null,
|
||||
coverUrl: item?.coverUrl || null,
|
||||
metadata: { playbackId: item?.id },
|
||||
});
|
||||
const videoPlayer = useVideoPlayer(videoUrl || null, (p) => {
|
||||
p.loop = false;
|
||||
p.muted = true;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useAudioPlayer } from "expo-audio";
|
||||
import useSharedAudioPlayer from "../../../hooks/useSharedAudioPlayer";
|
||||
import { BlurView } from "expo-blur";
|
||||
import React, {
|
||||
useCallback,
|
||||
@@ -144,7 +144,14 @@ const PlaybackItem = ({
|
||||
setIsLiked(currentUID ? lb.includes(currentUID) : false);
|
||||
}, [item?.likedBy, currentUID]);
|
||||
|
||||
const audioPlayer = useAudioPlayer(audioSource || undefined);
|
||||
const audioPlayer = useSharedAudioPlayer(audioSource || undefined, {
|
||||
id: item?.id ? `playback-${item.id}` : audioSource?.uri ? `playback-${audioSource.uri}` : undefined,
|
||||
title: typeof item?.title === "string" ? item.title : "",
|
||||
artist: typeof item?.userName === "string" ? item.userName : "",
|
||||
artwork: item?.coverUrl || null,
|
||||
coverUrl: item?.coverUrl || null,
|
||||
metadata: { playbackId: item?.id },
|
||||
});
|
||||
|
||||
// Helpers to normalize and map clocks
|
||||
const getAudioTimeSeconds = useCallback(() => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* global setInterval, clearInterval */
|
||||
import { useFocusEffect } from "@react-navigation/native";
|
||||
import { useAudioPlayer } from "expo-audio";
|
||||
import useSharedAudioPlayer from "../../hooks/useSharedAudioPlayer";
|
||||
import { BlurView } from "expo-blur";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { Image, Platform, Pressable, Text, View } from "react-native";
|
||||
@@ -34,11 +34,31 @@ const SongReady = () => {
|
||||
0: { pos: 0, dur: 0 },
|
||||
1: { pos: 0, dur: 0 },
|
||||
});
|
||||
const player0 = useAudioPlayer(
|
||||
musicUrls[0] ? { uri: musicUrls[0] } : undefined
|
||||
const player0 = useSharedAudioPlayer(
|
||||
musicUrls[0] ? { uri: musicUrls[0] } : undefined,
|
||||
{
|
||||
id: musicUrls[0] ? `songready-${musicUrls[0]}` : undefined,
|
||||
title:
|
||||
(Array.isArray(selectedProject?.musicTitles)
|
||||
? selectedProject?.musicTitles?.[0]
|
||||
: null) || selectedProject?.title || "Option 1",
|
||||
artwork: selectedProject?.coverUrl || null,
|
||||
coverUrl: selectedProject?.coverUrl || null,
|
||||
metadata: { index: 0, projectId },
|
||||
}
|
||||
);
|
||||
const player1 = useAudioPlayer(
|
||||
musicUrls[1] ? { uri: musicUrls[1] } : undefined
|
||||
const player1 = useSharedAudioPlayer(
|
||||
musicUrls[1] ? { uri: musicUrls[1] } : undefined,
|
||||
{
|
||||
id: musicUrls[1] ? `songready-${musicUrls[1]}` : undefined,
|
||||
title:
|
||||
(Array.isArray(selectedProject?.musicTitles)
|
||||
? selectedProject?.musicTitles?.[1]
|
||||
: null) || selectedProject?.title || "Option 2",
|
||||
artwork: selectedProject?.coverUrl || null,
|
||||
coverUrl: selectedProject?.coverUrl || null,
|
||||
metadata: { index: 1, projectId },
|
||||
}
|
||||
);
|
||||
const wasPlayingBeforeSeek = useRef({ 0: false, 1: false });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user