support for live mode

This commit is contained in:
SrGooglo 2025-02-20 02:42:03 +00:00
parent 05428959ff
commit 37b920b5ab

View File

@ -7,193 +7,210 @@ import useHideOnMouseStop from "@hooks/useHideOnMouseStop"
import { Icons } from "@components/Icons" import { Icons } from "@components/Icons"
import Controls from "@components/Player/Controls" import Controls from "@components/Player/Controls"
import LiveInfo from "@components/Player/LiveInfo"
import { usePlayerStateContext } from "@contexts/WithPlayerContext" import { usePlayerStateContext } from "@contexts/WithPlayerContext"
function isOverflown(element) { function isOverflown(element) {
if (!element) { if (!element) {
return false return false
} }
return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth; return (
element.scrollHeight > element.clientHeight ||
element.scrollWidth > element.clientWidth
)
} }
const PlayerController = React.forwardRef((props, ref) => { const PlayerController = React.forwardRef((props, ref) => {
const [playerState] = usePlayerStateContext() const [playerState] = usePlayerStateContext()
const titleRef = React.useRef() const titleRef = React.useRef()
const [hide, onMouseEnter, onMouseLeave] = useHideOnMouseStop({ delay: 3000, hideCursor: true }) const [hide, onMouseEnter, onMouseLeave] = useHideOnMouseStop({
const [titleIsOverflown, setTitleIsOverflown] = React.useState(false) delay: 3000,
hideCursor: true,
})
const [titleIsOverflown, setTitleIsOverflown] = React.useState(false)
const [currentTime, setCurrentTime] = React.useState(0) const [currentTime, setCurrentTime] = React.useState(0)
const [trackDuration, setTrackDuration] = React.useState(0) const [trackDuration, setTrackDuration] = React.useState(0)
const [draggingTime, setDraggingTime] = React.useState(false) const [draggingTime, setDraggingTime] = React.useState(false)
const [currentDragWidth, setCurrentDragWidth] = React.useState(0) const [currentDragWidth, setCurrentDragWidth] = React.useState(0)
const [syncInterval, setSyncInterval] = React.useState(null) const [syncInterval, setSyncInterval] = React.useState(null)
async function onDragEnd(seekTime) { async function onDragEnd(seekTime) {
setDraggingTime(false) setDraggingTime(false)
app.cores.player.controls.seek(seekTime) app.cores.player.controls.seek(seekTime)
syncPlayback() syncPlayback()
} }
async function syncPlayback() { async function syncPlayback() {
if (!playerState.track_manifest) { if (!playerState.track_manifest) {
return false return false
} }
const currentTrackTime = app.cores.player.controls.seek() const currentTrackTime = app.cores.player.controls.seek()
setCurrentTime(currentTrackTime) setCurrentTime(currentTrackTime)
} }
//* Handle when playback status change //* Handle when playback status change
React.useEffect(() => { React.useEffect(() => {
if (playerState.playback_status === "playing") { if (playerState.playback_status === "playing") {
setSyncInterval(setInterval(syncPlayback, 1000)) setSyncInterval(setInterval(syncPlayback, 1000))
} else { } else {
if (syncInterval) { if (syncInterval) {
clearInterval(syncInterval) clearInterval(syncInterval)
} }
} }
}, [playerState.playback_status]) }, [playerState.playback_status])
React.useEffect(() => { React.useEffect(() => {
setTitleIsOverflown(isOverflown(titleRef.current)) setTitleIsOverflown(isOverflown(titleRef.current))
setTrackDuration(app.cores.player.controls.duration()) setTrackDuration(app.cores.player.controls.duration())
}, [playerState.track_manifest]) }, [playerState.track_manifest])
React.useEffect(() => { React.useEffect(() => {
syncPlayback() syncPlayback()
}, []) }, [])
const isStopped = playerState.playback_status === "stopped" const isStopped = playerState.playback_status === "stopped"
return <div return (
className={classnames( <div
"lyrics-player-controller-wrapper", className={classnames("lyrics-player-controller-wrapper", {
{ ["hidden"]: props.lyrics?.video_source && hide,
["hidden"]: props.lyrics?.video_source && hide, })}
} onMouseEnter={onMouseEnter}
)} onMouseLeave={onMouseLeave}
onMouseEnter={onMouseEnter} >
onMouseLeave={onMouseLeave} <div className="lyrics-player-controller">
> <div className="lyrics-player-controller-info">
<div className="lyrics-player-controller"> <div className="lyrics-player-controller-info-title">
<div className="lyrics-player-controller-info"> {
<div className="lyrics-player-controller-info-title"> <h4
{ ref={titleRef}
<h4 className={classnames(
ref={titleRef} "lyrics-player-controller-info-title-text",
className={classnames( {
"lyrics-player-controller-info-title-text", ["overflown"]: titleIsOverflown,
{ },
["overflown"]: titleIsOverflown, )}
} >
)} {playerState.playback_status === "stopped" ||
> (!playerState.track_manifest?.title &&
{ "Nothing is playing")}
playerState.playback_status === "stopped" ? "Nothing is playing" : <>
{playerState.track_manifest?.title ?? "Nothing is playing"}
</>
}
</h4>
}
{ {playerState.playback_status !== "stopped" &&
titleIsOverflown && <Marquee playerState.track_manifest?.title}
//gradient </h4>
//gradientColor={bgColor} }
//gradientWidth={20}
play={!isStopped}
>
<h4>
{
isStopped ?
"Nothing is playing" :
<>
{playerState.track_manifest?.title ?? "Untitled"}
</>
}
</h4>
</Marquee>
}
</div>
<div className="lyrics-player-controller-info-details"> {titleIsOverflown && (
<span>{playerState.track_manifest?.artistStr}</span> <Marquee
</div> //gradient
</div> //gradientColor={bgColor}
//gradientWidth={20}
play={!isStopped}
>
<h4>
{isStopped ? (
"Nothing is playing"
) : (
<>
{playerState.track_manifest
?.title ?? "Untitled"}
</>
)}
</h4>
</Marquee>
)}
</div>
<Controls /> <div className="lyrics-player-controller-info-details">
<span>{playerState.track_manifest?.artistStr}</span>
</div>
<div className="lyrics-player-controller-progress-wrapper"> {playerState.live && (
<div <LiveInfo radioId={playerState.radioId} />
className="lyrics-player-controller-progress" )}
onMouseDown={(e) => { </div>
setDraggingTime(true)
}}
onMouseUp={(e) => {
const rect = e.currentTarget.getBoundingClientRect()
const seekTime = trackDuration * (e.clientX - rect.left) / rect.width
onDragEnd(seekTime) <Controls streamMode={playerState.live} />
}}
onMouseMove={(e) => {
const rect = e.currentTarget.getBoundingClientRect()
const atWidth = (e.clientX - rect.left) / rect.width * 100
setCurrentDragWidth(atWidth) {!playerState.live && (
}} <div className="lyrics-player-controller-progress-wrapper">
> <div
<div className="lyrics-player-controller-progress-bar" className="lyrics-player-controller-progress"
style={{ onMouseDown={(e) => {
width: `${draggingTime ? currentDragWidth : ((currentTime / trackDuration) * 100)}%` setDraggingTime(true)
}} }}
/> onMouseUp={(e) => {
</div> const rect =
</div> e.currentTarget.getBoundingClientRect()
const seekTime =
(trackDuration * (e.clientX - rect.left)) /
rect.width
<div className="lyrics-player-controller-tags"> onDragEnd(seekTime)
{ }}
playerState.track_manifest?.metadata.lossless && <Tag onMouseMove={(e) => {
icon={<Icons.Lossless const rect =
style={{ e.currentTarget.getBoundingClientRect()
margin:0, const atWidth =
}} ((e.clientX - rect.left) / rect.width) * 100
/>}
bordered={false} setCurrentDragWidth(atWidth)
/> }}
} >
{ <div
playerState.track_manifest?.explicit && <Tag className="lyrics-player-controller-progress-bar"
bordered={false} style={{
> width: `${draggingTime ? currentDragWidth : (currentTime / trackDuration) * 100}%`,
Explicit }}
</Tag> />
} </div>
{ </div>
props.lyrics?.sync_audio_at && <Tag )}
bordered={false}
icon={<Icons.TbMovie />} <div className="lyrics-player-controller-tags">
> {playerState.track_manifest?.metadata.lossless && (
Video <Tag
</Tag> icon={
} <Icons.Lossless
{ style={{
props.lyrics?.available_langs?.length > 1 && <Button margin: 0,
icon={<Icons.MdTranslate />} }}
type={props.translationEnabled ? "primary" : "default"} />
onClick={() => props.toggleTranslationEnabled()} }
size="small" bordered={false}
/> />
} )}
</div> {playerState.track_manifest?.explicit && (
</div> <Tag bordered={false}>Explicit</Tag>
</div> )}
{props.lyrics?.sync_audio_at && (
<Tag bordered={false} icon={<Icons.TbMovie />}>
Video
</Tag>
)}
{props.lyrics?.available_langs?.length > 1 && (
<Button
icon={<Icons.MdTranslate />}
type={
props.translationEnabled ? "primary" : "default"
}
onClick={() => props.toggleTranslationEnabled()}
size="small"
/>
)}
</div>
</div>
</div>
)
}) })
export default PlayerController export default PlayerController