global player

This commit is contained in:
2025-10-07 09:51:38 +02:00
parent 5cd6741ae6
commit f796d91267
15 changed files with 1031 additions and 136 deletions
+25 -117
View File
@@ -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(() => {