improve lyrics to use new manifest functions

This commit is contained in:
SrGooglo 2025-02-05 02:45:14 +00:00
parent 840d994480
commit 39b427dea7
5 changed files with 195 additions and 129 deletions

View File

@ -160,11 +160,13 @@ const PlayerController = React.forwardRef((props, ref) => {
<div className="lyrics-player-controller-tags"> <div className="lyrics-player-controller-tags">
{ {
playerState.track_manifest?.metadata.lossless && <Tag playerState.track_manifest?.metadata.lossless && <Tag
icon={<Icons.TbWaveSine />} icon={<Icons.Lossless
style={{
margin:0,
}}
/>}
bordered={false} bordered={false}
> />
Lossless
</Tag>
} }
{ {
playerState.track_manifest?.explicit && <Tag playerState.track_manifest?.explicit && <Tag

View File

@ -14,10 +14,6 @@ const LyricsText = React.forwardRef((props, textRef) => {
const [visible, setVisible] = React.useState(false) const [visible, setVisible] = React.useState(false)
function syncPlayback() { function syncPlayback() {
if (!lyrics) {
return false
}
const currentTrackTime = app.cores.player.controls.seek() * 1000 const currentTrackTime = app.cores.player.controls.seek() * 1000
const lineIndex = lyrics.synced_lyrics.findIndex((line) => { const lineIndex = lyrics.synced_lyrics.findIndex((line) => {
@ -46,9 +42,28 @@ const LyricsText = React.forwardRef((props, textRef) => {
} }
function startSyncInterval() { function startSyncInterval() {
if (!lyrics || !lyrics.synced_lyrics) {
stopSyncInterval()
return false
}
if (playerState.playback_status !== "playing") {
stopSyncInterval()
return false
}
if (syncInterval) {
stopSyncInterval()
}
setSyncInterval(setInterval(syncPlayback, 100)) setSyncInterval(setInterval(syncPlayback, 100))
} }
function stopSyncInterval() {
clearInterval(syncInterval)
setSyncInterval(null)
}
//* Handle when current line index change //* Handle when current line index change
React.useEffect(() => { React.useEffect(() => {
if (currentLineIndex === 0) { if (currentLineIndex === 0) {
@ -74,38 +89,19 @@ const LyricsText = React.forwardRef((props, textRef) => {
//* Handle when playback status change //* Handle when playback status change
React.useEffect(() => { React.useEffect(() => {
if (typeof lyrics?.synced_lyrics !== "undefined") {
if (playerState.playback_status === "playing") {
startSyncInterval() startSyncInterval()
} else {
if (syncInterval) {
clearInterval(syncInterval)
}
}
} else {
clearInterval(syncInterval)
}
}, [playerState.playback_status]) }, [playerState.playback_status])
//* Handle when lyrics object change //* Handle when manifest object change, reset...
React.useEffect(() => {
clearInterval(syncInterval)
if (lyrics) {
if (typeof lyrics?.synced_lyrics !== "undefined") {
if (playerState.playback_status === "playing") {
startSyncInterval()
}
}
}
}, [lyrics])
React.useEffect(() => { React.useEffect(() => {
setVisible(false) setVisible(false)
clearInterval(syncInterval)
setCurrentLineIndex(0) setCurrentLineIndex(0)
}, [playerState.track_manifest]) }, [playerState.track_manifest])
React.useEffect(() => {
startSyncInterval()
}, [lyrics])
React.useEffect(() => { React.useEffect(() => {
return () => { return () => {
clearInterval(syncInterval) clearInterval(syncInterval)

View File

@ -109,7 +109,12 @@ const LyricsVideo = React.forwardRef((props, videoRef) => {
if (lyrics) { if (lyrics) {
if (lyrics.video_source) { if (lyrics.video_source) {
console.log("Loading video source >", lyrics.video_source) console.log("Loading video source >", lyrics.video_source)
if (lyrics.video_source.endsWith(".mp4")) {
videoRef.current.src = lyrics.video_source
} else {
hls.current.loadSource(lyrics.video_source) hls.current.loadSource(lyrics.video_source)
}
if (typeof lyrics.sync_audio_at_ms !== "undefined") { if (typeof lyrics.sync_audio_at_ms !== "undefined") {
videoRef.current.loop = false videoRef.current.loop = false
@ -121,7 +126,7 @@ const LyricsVideo = React.forwardRef((props, videoRef) => {
videoRef.current.currentTime = 0 videoRef.current.currentTime = 0
} }
if (playerState.playback_status === "playing"){ if (playerState.playback_status === "playing") {
videoRef.current.play() videoRef.current.play()
} }
} }

View File

@ -4,41 +4,75 @@ import classnames from "classnames"
import useMaxScreen from "@hooks/useMaxScreen" import useMaxScreen from "@hooks/useMaxScreen"
import { usePlayerStateContext } from "@contexts/WithPlayerContext" import { usePlayerStateContext } from "@contexts/WithPlayerContext"
import MusicService from "@models/music"
import PlayerController from "./components/controller" import PlayerController from "./components/controller"
import LyricsVideo from "./components/video" import LyricsVideo from "./components/video"
import LyricsText from "./components/text" import LyricsText from "./components/text"
import "./index.less" import "./index.less"
function getDominantColorStr(track_manifest) { function getDominantColorStr(analysis) {
if (!track_manifest) { if (!analysis) {
return `0,0,0` return `0,0,0`
} }
const values = track_manifest.cover_analysis?.value ?? [0, 0, 0] const values = analysis?.value ?? [0, 0, 0]
return `${values[0]}, ${values[1]}, ${values[2]}` return `${values[0]}, ${values[1]}, ${values[2]}`
} }
function toggleFullScreen(to) {
to = to ?? !document.fullscreenElement
if (to === true) {
document.documentElement.requestFullscreen().catch((err) => {
console.log(`Failed to set to fullscreen: ${err.message}`)
})
} else {
try {
document.exitFullscreen()
} catch (error) {
// xd
}
}
}
const EnchancedLyricsPage = () => { const EnchancedLyricsPage = () => {
const [playerState] = usePlayerStateContext() const [playerState] = usePlayerStateContext()
const [trackManifest, setTrackManifest] = React.useState(null)
const [initialized, setInitialized] = React.useState(false) 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 videoRef = React.useRef() const videoRef = React.useRef()
const textRef = React.useRef() const textRef = React.useRef()
async function loadLyrics(track_id) { function listenFullScreenChange() {
const result = await MusicService.getTrackLyrics(track_id, { if (!document.fullscreenElement) {
if (app.location.last) {
app.location.back()
} else {
app.navigation.goMain()
}
}
}
async function loadCurrentTrackLyrics() {
// get current track instance
const instance = app.cores.player.track()
const result = await instance.manifest.serviceOperations
.fetchLyrics({
preferTranslation: translationEnabled, preferTranslation: translationEnabled,
}).catch((err) => { })
.catch((err) => {
console.error("Failed to fetch lyrics", err)
return null return null
}) })
console.log("Fetched Lyrics >", result)
if (result) { if (result) {
setLyrics(result) setLyrics(result)
} else { } else {
@ -54,67 +88,95 @@ const EnchancedLyricsPage = () => {
useMaxScreen() useMaxScreen()
React.useEffect((prev) => { // React.useEffect((prev) => {
if (initialized) { // if (initialized) {
loadLyrics(playerState.track_manifest._id) // loadLyrics(playerState.track_manifest)
} // }
}, [translationEnabled]) // }, [translationEnabled])
//* Handle when context change track_manifest //* Handle when context change track_manifest
React.useEffect(() => { React.useEffect(() => {
if (playerState.track_manifest) { if (trackManifest && playerState.track_manifest) {
if (!lyrics || (lyrics.track_id !== playerState.track_manifest._id)) { if (!lyrics || lyrics.track_id !== playerState.track_manifest._id) {
loadLyrics(playerState.track_manifest._id) loadCurrentTrackLyrics()
} }
} else { } else {
setLyrics(null) 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(() => { React.useEffect(() => {
setInitialized(true) setInitialized(true)
toggleFullScreen(true)
document.addEventListener("fullscreenchange", listenFullScreenChange)
return () => {
toggleFullScreen(false)
document.removeEventListener(
"fullscreenchange",
listenFullScreenChange,
)
}
}, []) }, [])
return <div return (
className={classnames( <div
"lyrics", className={classnames("lyrics", {
{
["stopped"]: playerState.playback_status !== "playing", ["stopped"]: playerState.playback_status !== "playing",
} })}
)}
style={{ style={{
"--dominant-color": getDominantColorStr(playerState.track_manifest) "--dominant-color": getDominantColorStr(coverAnalysis),
}} }}
> >
<div <div className="lyrics-background-color" />
className="lyrics-background-color"
/>
{ {playerState.track_manifest && !lyrics?.video_source && (
playerState.track_manifest && !lyrics?.video_source && <div <div className="lyrics-background-wrapper">
className="lyrics-background-wrapper" <div className="lyrics-background-cover">
> <img src={playerState.track_manifest.cover} />
<div
className="lyrics-background-cover"
>
<img
src={playerState.track_manifest.cover}
/>
</div> </div>
</div> </div>
} )}
<LyricsVideo <LyricsVideo ref={videoRef} lyrics={lyrics} />
ref={videoRef}
lyrics={lyrics}
/>
<LyricsText <LyricsText ref={textRef} lyrics={lyrics} />
ref={textRef}
lyrics={lyrics}
translationEnabled={translationEnabled}
/>
<PlayerController <PlayerController
lyrics={lyrics} lyrics={lyrics}
@ -122,6 +184,7 @@ const EnchancedLyricsPage = () => {
toggleTranslationEnabled={toggleTranslationEnabled} toggleTranslationEnabled={toggleTranslationEnabled}
/> />
</div> </div>
)
} }
export default EnchancedLyricsPage export default EnchancedLyricsPage

View File

@ -58,7 +58,7 @@
width: 40vw; width: 40vw;
height: 40vw; height: 40vw;
object-fit: contain; object-fit: cover;
border-radius: 24px; border-radius: 24px;
} }