diff --git a/src/screens/Library/MusicDetails.js b/src/screens/Library/MusicDetails.js index 963328a..a289355 100644 --- a/src/screens/Library/MusicDetails.js +++ b/src/screens/Library/MusicDetails.js @@ -189,6 +189,31 @@ const MusicDetails = ({ route }) => { console.log("MusicDetails seekBy error", e?.message); } }; + + const handleLyricsSeek = React.useCallback( + async (timestampS) => { + if (!player || typeof timestampS !== "number" || Number.isNaN(timestampS)) + return; + const seconds = Math.max(0, Number(timestampS) || 0); + const duration = Number(player?.duration || 0); + const bounded = duration > 0 ? Math.min(seconds, duration) : seconds; + + try { + const wasPlaying = !!player?.playing; + await player.seekTo?.(bounded); + setProgressInfo((prev) => ({ ...prev, pos: bounded * 1000 })); + if (wasPlaying) { + if (!player?.playing) { + await player.play?.(); + } + setIsPlaying(true); + } + } catch (e) { + console.log("MusicDetails lyrics seek error", e?.message); + } + }, + [player] + ); const description = useMemo(() => { // Build a readable text from lyrics with section labels if (Array.isArray(project?.lyrics)) { @@ -496,6 +521,7 @@ const MusicDetails = ({ route }) => { ? styles.karaokeWordActive : styles.karaokeWord } + onPress={() => handleLyricsSeek(w.startS)} > {w.text} {j < (L.words?.length || 1) - 1 ? " " : ""} diff --git a/src/screens/Library/MusicDetails.web.js b/src/screens/Library/MusicDetails.web.js index 5f5e6fe..bebeec2 100644 --- a/src/screens/Library/MusicDetails.web.js +++ b/src/screens/Library/MusicDetails.web.js @@ -289,6 +289,26 @@ const MusicDetails = ({ route }) => { console.log("MusicDetails seekBy error", e?.message); } }; + + const handleLyricsSeek = React.useCallback( + async (timestampS) => { + if (typeof timestampS !== "number" || Number.isNaN(timestampS)) return; + const seconds = Math.max(0, Number(timestampS) || 0); + const durationS = durationMs > 0 ? durationMs / 1000 : null; + const bounded = durationS ? Math.min(seconds, durationS) : seconds; + + try { + const wasPlaying = isPlaying; + seekAudio(bounded); + if (wasPlaying) { + await playAudio(); + } + } catch (e) { + console.log("MusicDetails lyrics seek error", e?.message); + } + }, + [durationMs, isPlaying, playAudio, seekAudio] + ); const description = useMemo(() => { // Build a readable text from lyrics with section labels if (Array.isArray(project?.lyrics)) { @@ -644,6 +664,7 @@ const MusicDetails = ({ route }) => { ? styles.karaokeWordActive : styles.karaokeWord } + onPress={() => handleLyricsSeek(w.startS)} > {w.text} {j < (L.words?.length || 1) - 1 ? " " : ""} @@ -706,9 +727,11 @@ const styles = StyleSheet.create({ karaokeWord: { color: Palette.white, fontFamily: FONT_FAMILY.InterRegular, + cursor: "pointer", }, karaokeWordActive: { color: Palette.primary, fontFamily: FONT_FAMILY.InterBold, + cursor: "pointer", }, });