refactor & improve

This commit is contained in:
SrGooglo 2025-02-20 02:43:22 +00:00
parent 37b920b5ab
commit 0589a11371

View File

@ -1,8 +1,7 @@
import React from "react" import React, { useCallback, useEffect, useMemo, useRef } from "react"
import classnames from "classnames" import classnames from "classnames"
import parseTimeToMs from "@utils/parseTimeToMs" import parseTimeToMs from "@utils/parseTimeToMs"
import useMaxScreen from "@hooks/useMaxScreen" import useMaxScreen from "@hooks/useMaxScreen"
import { usePlayerStateContext } from "@contexts/WithPlayerContext" import { usePlayerStateContext } from "@contexts/WithPlayerContext"
@ -12,185 +11,180 @@ import LyricsText from "./components/text"
import "./index.less" import "./index.less"
function getDominantColorStr(analysis) { const getDominantColorStr = (analysis) => {
if (!analysis) { if (!analysis) return "0,0,0"
return `0,0,0` return analysis.value?.join(", ") || "0,0,0"
}
const values = analysis?.value ?? [0, 0, 0]
return `${values[0]}, ${values[1]}, ${values[2]}`
} }
function toggleFullScreen(to) { const toggleFullScreen = (to) => {
to = to ?? !document.fullscreenElement const targetState = to ?? !document.fullscreenElement
if (to === true) { try {
document.documentElement.requestFullscreen().catch((err) => { if (targetState) {
console.log(`Failed to set to fullscreen: ${err.message}`) document.documentElement.requestFullscreen()
}) } else if (document.fullscreenElement) {
} else {
try {
document.exitFullscreen() document.exitFullscreen()
} catch (error) {
// xd
} }
} catch (error) {
console.error("Fullscreen toggle failed:", error)
} }
} }
const EnchancedLyricsPage = () => { const EnhancedLyricsPage = () => {
useMaxScreen()
const [playerState] = usePlayerStateContext() const [playerState] = usePlayerStateContext()
const [trackManifest, setTrackManifest] = React.useState(null) const [trackManifest, setTrackManifest] = React.useState(null)
const [initialized, setInitialized] = React.useState(false)
const [lyrics, setLyrics] = React.useState(null) const [lyrics, setLyrics] = React.useState(null)
const [translationEnabled, setTranslationEnabled] = React.useState(false) const [translationEnabled, setTranslationEnabled] = React.useState(false)
const [coverAnalysis, setCoverAnalysis] = React.useState(null) const [coverAnalysis, setCoverAnalysis] = React.useState(null)
const videoRef = React.useRef() const videoRef = useRef()
const textRef = React.useRef() const textRef = useRef()
const isMounted = useRef(true)
const currentTrackId = useRef(null)
function listenFullScreenChange() { const dominantColor = useMemo(
if (!document.fullscreenElement) { () => ({ "--dominant-color": getDominantColorStr(coverAnalysis) }),
if (app.location.last) { [coverAnalysis],
app.location.back() )
} else {
app.navigation.goMain() const handleFullScreenChange = useCallback(() => {
} if (!document.fullscreenElement && app?.location?.last) {
app.location.back()
} }
} }, [])
const loadCurrentTrackLyrics = useCallback(async () => {
if (!playerState.track_manifest) return
async function loadCurrentTrackLyrics() {
// get current track instance
const instance = app.cores.player.track() const instance = app.cores.player.track()
if (!instance) return
let result = await instance.manifest.serviceOperations try {
.fetchLyrics({ const result =
preferTranslation: translationEnabled, await instance.manifest.serviceOperations.fetchLyrics({
}) preferTranslation: translationEnabled,
.catch((err) => { })
console.error("Failed to fetch lyrics", err)
return null
})
if (result.sync_audio_at && !result.sync_audio_at_ms) { if (!isMounted.current) return
result.sync_audio_at_ms = parseTimeToMs(result.sync_audio_at)
}
console.log("Fetched Lyrics >", result) const processedLyrics =
result.sync_audio_at && !result.sync_audio_at_ms
? {
...result,
sync_audio_at_ms: parseTimeToMs(
result.sync_audio_at,
),
}
: result
if (result) { console.log("Fetched Lyrics >", processedLyrics)
setLyrics(result) setLyrics(processedLyrics || false)
} else { } catch (error) {
console.error("Failed to fetch lyrics", error)
setLyrics(false) setLyrics(false)
} }
} }, [translationEnabled, playerState.track_manifest])
async function toggleTranslationEnabled(to) { // Track manifest comparison
setTranslationEnabled((prev) => { useEffect(() => {
return to ?? !prev const newManifest = playerState.track_manifest?.toSeriableObject()
}) if (JSON.stringify(newManifest) !== JSON.stringify(trackManifest)) {
} setTrackManifest(newManifest)
useMaxScreen()
// React.useEffect((prev) => {
// if (initialized) {
// loadLyrics(playerState.track_manifest)
// }
// }, [translationEnabled])
//* Handle when context change track_manifest
React.useEffect(() => {
if (trackManifest && playerState.track_manifest) {
if (!lyrics || lyrics.track_id !== playerState.track_manifest._id) {
loadCurrentTrackLyrics()
}
} else {
setLyrics(null)
}
}, [trackManifest])
React.useEffect(() => {
if (!playerState.track_manifest) {
return
}
const currentPlayerTrackManifest =
playerState.track_manifest.toSeriableObject()
// check if track manifest is the same
if (trackManifest === currentPlayerTrackManifest) {
return
}
setTrackManifest(currentPlayerTrackManifest)
}, [playerState])
React.useEffect(() => {
const trackInstance = app.cores.player.track()
if (playerState.track_manifest && trackInstance) {
if (
typeof trackInstance.manifest.analyzeCoverColor === "function"
) {
trackInstance.manifest
.analyzeCoverColor()
.then((analysis) => {
setCoverAnalysis(analysis)
})
.catch((err) => {
console.error("Failed to get cover analysis", err)
})
}
} }
}, [playerState.track_manifest]) }, [playerState.track_manifest])
React.useEffect(() => { // Lyrics loading trigger
setInitialized(true) useEffect(() => {
toggleFullScreen(true) if (!trackManifest) {
setLyrics(null)
return
}
document.addEventListener("fullscreenchange", listenFullScreenChange) if (!lyrics || lyrics.track_id !== trackManifest._id) {
loadCurrentTrackLyrics()
}
}, [trackManifest, lyrics?.track_id])
// Cover analysis
useEffect(() => {
const getCoverAnalysis = async () => {
const trackInstance = app.cores.player.track()
if (!trackInstance?.manifest.analyzeCoverColor) return
try {
const analysis =
await trackInstance.manifest.analyzeCoverColor()
if (isMounted.current) setCoverAnalysis(analysis)
} catch (error) {
console.error("Failed to get cover analysis", error)
}
}
if (playerState.track_manifest) {
getCoverAnalysis()
}
}, [playerState.track_manifest])
// Initialization and cleanup
useEffect(() => {
isMounted.current = true
toggleFullScreen(true)
document.addEventListener("fullscreenchange", handleFullScreenChange)
return () => { return () => {
isMounted.current = false
toggleFullScreen(false) toggleFullScreen(false)
document.removeEventListener( document.removeEventListener(
"fullscreenchange", "fullscreenchange",
listenFullScreenChange, handleFullScreenChange,
) )
} }
}, []) }, [])
// Translation toggler
const handleTranslationToggle = useCallback(
(to) => setTranslationEnabled((prev) => to ?? !prev),
[],
)
// Memoized background component
const renderBackground = useMemo(() => {
if (!playerState.track_manifest || lyrics?.video_source) return null
return (
<div className="lyrics-background-wrapper">
<div className="lyrics-background-cover">
<img
src={playerState.track_manifest.cover}
alt="Album cover"
/>
</div>
</div>
)
}, [playerState.track_manifest, lyrics?.video_source])
return ( return (
<div <div
className={classnames("lyrics", { className={classnames("lyrics", {
["stopped"]: playerState.playback_status !== "playing", stopped: playerState.playback_status !== "playing",
})} })}
style={{ style={dominantColor}
"--dominant-color": getDominantColorStr(coverAnalysis),
}}
> >
<div className="lyrics-background-color" /> <div className="lyrics-background-color" />
{playerState.track_manifest && !lyrics?.video_source && ( {renderBackground}
<div className="lyrics-background-wrapper">
<div className="lyrics-background-cover">
<img src={playerState.track_manifest.cover} />
</div>
</div>
)}
<LyricsVideo ref={videoRef} lyrics={lyrics} /> <LyricsVideo ref={videoRef} lyrics={lyrics} />
<LyricsText ref={textRef} lyrics={lyrics} /> <LyricsText ref={textRef} lyrics={lyrics} />
<PlayerController <PlayerController
lyrics={lyrics} lyrics={lyrics}
translationEnabled={translationEnabled} translationEnabled={translationEnabled}
toggleTranslationEnabled={toggleTranslationEnabled} toggleTranslationEnabled={handleTranslationToggle}
/> />
</div> </div>
) )
} }
export default EnchancedLyricsPage export default EnhancedLyricsPage