mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 10:34:17 +00:00
Support for live radios
This commit is contained in:
parent
5f3fb3a013
commit
6eef3480b1
@ -14,113 +14,122 @@ import { usePlayerStateContext } from "@contexts/WithPlayerContext"
|
||||
import "./index.less"
|
||||
|
||||
const EventsHandlers = {
|
||||
"playback": () => {
|
||||
return app.cores.player.playback.toggle()
|
||||
},
|
||||
"previous": () => {
|
||||
return app.cores.player.playback.previous()
|
||||
},
|
||||
"next": () => {
|
||||
return app.cores.player.playback.next()
|
||||
},
|
||||
"volume": (ctx, value) => {
|
||||
return app.cores.player.controls.volume(value)
|
||||
},
|
||||
"mute": () => {
|
||||
return app.cores.player.controls.mute("toggle")
|
||||
},
|
||||
"like": async (ctx) => {
|
||||
if (!ctx.track_manifest) {
|
||||
return false
|
||||
}
|
||||
playback: (state) => {
|
||||
if (state.live) {
|
||||
return false
|
||||
}
|
||||
return app.cores.player.playback.toggle()
|
||||
},
|
||||
previous: () => {
|
||||
return app.cores.player.playback.previous()
|
||||
},
|
||||
next: () => {
|
||||
return app.cores.player.playback.next()
|
||||
},
|
||||
volume: (ctx, value) => {
|
||||
return app.cores.player.controls.volume(value)
|
||||
},
|
||||
mute: () => {
|
||||
return app.cores.player.controls.mute("toggle")
|
||||
},
|
||||
like: async (ctx) => {
|
||||
if (!ctx.track_manifest) {
|
||||
return false
|
||||
}
|
||||
|
||||
const track = app.cores.player.track()
|
||||
const track = app.cores.player.track()
|
||||
|
||||
return await track.manifest.serviceOperations.toggleItemFavourite("track", ctx.track_manifest._id)
|
||||
},
|
||||
return await track.manifest.serviceOperations.toggleItemFavourite(
|
||||
"track",
|
||||
ctx.track_manifest._id,
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
const Controls = (props) => {
|
||||
const [playerState] = usePlayerStateContext()
|
||||
const [playerState] = usePlayerStateContext()
|
||||
|
||||
const handleAction = (event, ...args) => {
|
||||
if (typeof EventsHandlers[event] !== "function") {
|
||||
throw new Error(`Unknown event "${event}"`)
|
||||
}
|
||||
const handleAction = (event, ...args) => {
|
||||
if (typeof EventsHandlers[event] !== "function") {
|
||||
throw new Error(`Unknown event "${event}"`)
|
||||
}
|
||||
|
||||
return EventsHandlers[event](playerState, ...args)
|
||||
}
|
||||
return EventsHandlers[event](playerState, ...args)
|
||||
}
|
||||
|
||||
return <div
|
||||
className={
|
||||
props.className ?? "player-controls"
|
||||
}
|
||||
>
|
||||
<AudioPlayerChangeModeButton
|
||||
disabled={playerState.control_locked}
|
||||
/>
|
||||
<antd.Button
|
||||
type="ghost"
|
||||
shape="round"
|
||||
icon={<Icons.FiChevronLeft />}
|
||||
onClick={() => handleAction("previous")}
|
||||
disabled={playerState.control_locked}
|
||||
/>
|
||||
<antd.Button
|
||||
type="primary"
|
||||
shape="circle"
|
||||
icon={playerState.livestream_mode ? <Icons.MdStop /> : playerState.playback_status === "playing" ? <Icons.MdPause /> : <Icons.MdPlayArrow />}
|
||||
onClick={() => handleAction("playback")}
|
||||
className="playButton"
|
||||
disabled={playerState.control_locked}
|
||||
>
|
||||
{
|
||||
playerState.loading && <div className="loadCircle">
|
||||
<UseAnimations
|
||||
animation={LoadingAnimation}
|
||||
size="100%"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</antd.Button>
|
||||
<antd.Button
|
||||
type="ghost"
|
||||
shape="round"
|
||||
icon={<Icons.FiChevronRight />}
|
||||
onClick={() => handleAction("next")}
|
||||
disabled={playerState.control_locked}
|
||||
/>
|
||||
{
|
||||
!app.isMobile && <antd.Popover
|
||||
content={React.createElement(
|
||||
AudioVolume,
|
||||
{
|
||||
onChange: (value) => handleAction("volume", value),
|
||||
defaultValue: playerState.volume
|
||||
}
|
||||
)}
|
||||
trigger="hover"
|
||||
>
|
||||
<button
|
||||
className="muteButton"
|
||||
onClick={() => handleAction("mute")}
|
||||
>
|
||||
{
|
||||
playerState.muted
|
||||
? <Icons.FiVolumeX />
|
||||
: <Icons.FiVolume2 />
|
||||
}
|
||||
</button>
|
||||
</antd.Popover>
|
||||
}
|
||||
return (
|
||||
<div className={props.className ?? "player-controls"}>
|
||||
<AudioPlayerChangeModeButton disabled={props.streamMode} />
|
||||
<antd.Button
|
||||
type="ghost"
|
||||
shape="round"
|
||||
icon={<Icons.FiChevronLeft />}
|
||||
onClick={() => handleAction("previous")}
|
||||
disabled={props.streamMode}
|
||||
/>
|
||||
<antd.Button
|
||||
type="primary"
|
||||
shape="circle"
|
||||
icon={
|
||||
props.streamMode ? (
|
||||
<Icons.MdStop />
|
||||
) : playerState.playback_status === "playing" ? (
|
||||
<Icons.MdPause />
|
||||
) : (
|
||||
<Icons.MdPlayArrow />
|
||||
)
|
||||
}
|
||||
onClick={() => handleAction("playback")}
|
||||
className="playButton"
|
||||
>
|
||||
{playerState.loading && (
|
||||
<div className="loadCircle">
|
||||
<UseAnimations
|
||||
animation={LoadingAnimation}
|
||||
size="100%"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</antd.Button>
|
||||
<antd.Button
|
||||
type="ghost"
|
||||
shape="round"
|
||||
icon={<Icons.FiChevronRight />}
|
||||
onClick={() => handleAction("next")}
|
||||
disabled={props.streamMode}
|
||||
/>
|
||||
{!app.isMobile && (
|
||||
<antd.Popover
|
||||
content={React.createElement(AudioVolume, {
|
||||
onChange: (value) => handleAction("volume", value),
|
||||
defaultValue: playerState.volume,
|
||||
})}
|
||||
trigger="hover"
|
||||
>
|
||||
<button
|
||||
className="muteButton"
|
||||
onClick={() => handleAction("mute")}
|
||||
>
|
||||
{playerState.muted ? (
|
||||
<Icons.FiVolumeX />
|
||||
) : (
|
||||
<Icons.FiVolume2 />
|
||||
)}
|
||||
</button>
|
||||
</antd.Popover>
|
||||
)}
|
||||
|
||||
{
|
||||
app.isMobile && <LikeButton
|
||||
liked={playerState.track_manifest?.serviceOperations.fetchLikeStatus}
|
||||
onClick={() => handleAction("like")}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
{app.isMobile && (
|
||||
<LikeButton
|
||||
liked={
|
||||
playerState.track_manifest?.serviceOperations
|
||||
.fetchLikeStatus
|
||||
}
|
||||
onClick={() => handleAction("like")}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Controls
|
||||
export default Controls
|
||||
|
@ -7,40 +7,46 @@ import LikeButton from "@components/LikeButton"
|
||||
import { usePlayerStateContext } from "@contexts/WithPlayerContext"
|
||||
|
||||
const ExtraActions = (props) => {
|
||||
const [playerState] = usePlayerStateContext()
|
||||
const [playerState] = usePlayerStateContext()
|
||||
|
||||
const handleClickLike = async () => {
|
||||
if (!playerState.track_manifest) {
|
||||
console.error("Cannot like a track if nothing is playing")
|
||||
return false
|
||||
}
|
||||
const handleClickLike = async () => {
|
||||
if (!playerState.track_manifest) {
|
||||
console.error("Cannot like a track if nothing is playing")
|
||||
return false
|
||||
}
|
||||
|
||||
const track = app.cores.player.track()
|
||||
const track = app.cores.player.track()
|
||||
|
||||
await track.manifest.serviceOperations.toggleItemFavourite("track", playerState.track_manifest._id)
|
||||
}
|
||||
await track.manifest.serviceOperations.toggleItemFavourite(
|
||||
"track",
|
||||
playerState.track_manifest._id,
|
||||
)
|
||||
}
|
||||
|
||||
return <div className="extra_actions">
|
||||
{
|
||||
app.isMobile && <Button
|
||||
type="ghost"
|
||||
icon={<Icons.MdAbc />}
|
||||
disabled={!playerState.track_manifest?.lyrics_enabled}
|
||||
/>
|
||||
}
|
||||
return (
|
||||
<div className="extra_actions">
|
||||
{app.isMobile && (
|
||||
<Button
|
||||
type="ghost"
|
||||
icon={<Icons.MdAbc />}
|
||||
disabled={!playerState.track_manifest?.lyrics_enabled}
|
||||
/>
|
||||
)}
|
||||
|
||||
{
|
||||
!app.isMobile && playerState.track_manifest?._id && <LikeButton
|
||||
liked={playerState.track_manifest?.serviceOperations.fetchLikeStatus}
|
||||
onClick={handleClickLike}
|
||||
/>
|
||||
}
|
||||
{!app.isMobile && (
|
||||
<LikeButton
|
||||
liked={
|
||||
playerState.track_manifest?.serviceOperations
|
||||
.fetchLikeStatus
|
||||
}
|
||||
onClick={handleClickLike}
|
||||
disabled={!playerState.track_manifest?._id}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="ghost"
|
||||
icon={<Icons.MdQueueMusic />}
|
||||
/>
|
||||
</div>
|
||||
<Button type="ghost" icon={<Icons.MdQueueMusic />} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ExtraActions
|
||||
export default ExtraActions
|
||||
|
51
packages/app/src/components/Player/LiveInfo/index.jsx
Normal file
51
packages/app/src/components/Player/LiveInfo/index.jsx
Normal file
@ -0,0 +1,51 @@
|
||||
import React from "react"
|
||||
import SSEEvents from "@classes/SSEEvents"
|
||||
|
||||
import { MdPlayCircle, MdHeadphones } from "react-icons/md"
|
||||
|
||||
import "./index.less"
|
||||
|
||||
const LiveInfo = ({ radioId, initialData }) => {
|
||||
const [data, setData] = React.useState(initialData ?? {})
|
||||
|
||||
const eventManager = React.useMemo(
|
||||
() =>
|
||||
new SSEEvents(
|
||||
`${app.cores.api.client().mainOrigin}/music/radio/sse/radio:${radioId}`,
|
||||
{
|
||||
update: (data) => {
|
||||
if (typeof data.now_playing === "string") {
|
||||
data.now_playing = JSON.parse(data.now_playing)
|
||||
}
|
||||
|
||||
console.log(`Radio data updated`, data)
|
||||
setData(data)
|
||||
},
|
||||
},
|
||||
),
|
||||
[],
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
eventManager.close()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="live-info">
|
||||
{data.now_playing && (
|
||||
<>
|
||||
<div className="live-info-title">
|
||||
<MdPlayCircle /> {data.now_playing.song.text}
|
||||
</div>
|
||||
<div className="live-info-listeners">
|
||||
<MdHeadphones /> {data.listeners}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LiveInfo
|
8
packages/app/src/components/Player/LiveInfo/index.less
Normal file
8
packages/app/src/components/Player/LiveInfo/index.less
Normal file
@ -0,0 +1,8 @@
|
||||
.live-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
gap: 7px;
|
||||
|
||||
height: 100%;
|
||||
}
|
@ -8,212 +8,218 @@ import seekToTimeLabel from "@utils/seekToTimeLabel"
|
||||
import "./index.less"
|
||||
|
||||
export default class SeekBar extends React.Component {
|
||||
state = {
|
||||
playing: app.cores.player.state["playback_status"] === "playing",
|
||||
timeText: "00:00",
|
||||
durationText: "00:00",
|
||||
sliderTime: 0,
|
||||
sliderLock: false,
|
||||
}
|
||||
state = {
|
||||
playing: app.cores.player.state["playback_status"] === "playing",
|
||||
timeText: "00:00",
|
||||
durationText: "00:00",
|
||||
sliderTime: 0,
|
||||
sliderLock: false,
|
||||
}
|
||||
|
||||
handleSeek = (value) => {
|
||||
if (value > 0) {
|
||||
// calculate the duration of the audio
|
||||
const duration = app.cores.player.controls.duration()
|
||||
handleSeek = (value) => {
|
||||
if (value > 0) {
|
||||
// calculate the duration of the audio
|
||||
const duration = app.cores.player.controls.duration()
|
||||
|
||||
// calculate the seek of the audio
|
||||
const seek = (value / 100) * duration
|
||||
// calculate the seek of the audio
|
||||
const seek = (value / 100) * duration
|
||||
|
||||
app.cores.player.controls.seek(seek)
|
||||
} else {
|
||||
app.cores.player.controls.seek(0)
|
||||
}
|
||||
}
|
||||
app.cores.player.controls.seek(seek)
|
||||
} else {
|
||||
app.cores.player.controls.seek(0)
|
||||
}
|
||||
}
|
||||
|
||||
calculateDuration = (preCalculatedDuration) => {
|
||||
// get current audio duration
|
||||
const audioDuration = preCalculatedDuration ?? app.cores.player.controls.duration()
|
||||
calculateDuration = (preCalculatedDuration) => {
|
||||
// get current audio duration
|
||||
const audioDuration =
|
||||
preCalculatedDuration ?? app.cores.player.controls.duration()
|
||||
|
||||
if (isNaN(audioDuration)) {
|
||||
return
|
||||
}
|
||||
if (isNaN(audioDuration)) {
|
||||
return
|
||||
}
|
||||
|
||||
// set duration
|
||||
this.setState({
|
||||
durationText: seekToTimeLabel(audioDuration)
|
||||
})
|
||||
}
|
||||
// set duration
|
||||
this.setState({
|
||||
durationText: seekToTimeLabel(audioDuration),
|
||||
})
|
||||
}
|
||||
|
||||
calculateTime = () => {
|
||||
// get current audio seek
|
||||
const seek = app.cores.player.controls.seek()
|
||||
calculateTime = () => {
|
||||
// get current audio seek
|
||||
const seek = app.cores.player.controls.seek()
|
||||
|
||||
// set time
|
||||
this.setState({
|
||||
timeText: seekToTimeLabel(seek)
|
||||
})
|
||||
}
|
||||
// set time
|
||||
this.setState({
|
||||
timeText: seekToTimeLabel(seek),
|
||||
})
|
||||
}
|
||||
|
||||
updateProgressBar = () => {
|
||||
if (this.state.sliderLock) {
|
||||
return
|
||||
}
|
||||
updateProgressBar = () => {
|
||||
if (this.state.sliderLock) {
|
||||
return
|
||||
}
|
||||
|
||||
const seek = app.cores.player.controls.seek()
|
||||
const duration = app.cores.player.controls.duration()
|
||||
const seek = app.cores.player.controls.seek()
|
||||
const duration = app.cores.player.controls.duration()
|
||||
|
||||
const percent = (seek / duration) * 100
|
||||
const percent = (seek / duration) * 100
|
||||
|
||||
this.setState({
|
||||
sliderTime: percent
|
||||
})
|
||||
}
|
||||
this.setState({
|
||||
sliderTime: percent,
|
||||
})
|
||||
}
|
||||
|
||||
updateAll = () => {
|
||||
this.calculateTime()
|
||||
this.updateProgressBar()
|
||||
}
|
||||
updateAll = () => {
|
||||
this.calculateTime()
|
||||
this.updateProgressBar()
|
||||
}
|
||||
|
||||
events = {
|
||||
// handle when player changes playback status
|
||||
"player.state.update:playback_status": (status) => {
|
||||
this.setState({
|
||||
playing: status === "playing",
|
||||
})
|
||||
events = {
|
||||
// handle when player changes playback status
|
||||
"player.state.update:playback_status": (status) => {
|
||||
this.setState({
|
||||
playing: status === "playing",
|
||||
})
|
||||
|
||||
switch (status) {
|
||||
case "stopped":
|
||||
this.setState({
|
||||
timeText: "00:00",
|
||||
durationText: "00:00",
|
||||
sliderTime: 0,
|
||||
})
|
||||
switch (status) {
|
||||
case "stopped":
|
||||
this.setState({
|
||||
timeText: "00:00",
|
||||
durationText: "00:00",
|
||||
sliderTime: 0,
|
||||
})
|
||||
|
||||
break
|
||||
case "playing":
|
||||
this.updateAll()
|
||||
this.calculateDuration()
|
||||
break
|
||||
case "playing":
|
||||
this.updateAll()
|
||||
this.calculateDuration()
|
||||
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
},
|
||||
// handle when player changes track
|
||||
"player.state.update:track_manifest": (manifest) => {
|
||||
if (!manifest) {
|
||||
return false
|
||||
}
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
},
|
||||
// handle when player changes track
|
||||
"player.state.update:track_manifest": (manifest) => {
|
||||
if (!manifest) {
|
||||
return false
|
||||
}
|
||||
|
||||
this.updateAll()
|
||||
this.updateAll()
|
||||
|
||||
this.setState({
|
||||
timeText: "00:00",
|
||||
sliderTime: 0,
|
||||
})
|
||||
this.setState({
|
||||
timeText: "00:00",
|
||||
sliderTime: 0,
|
||||
})
|
||||
|
||||
this.calculateDuration(manifest.metadata?.duration ?? manifest.duration)
|
||||
},
|
||||
"player.seeked": (seek) => {
|
||||
this.calculateTime()
|
||||
this.updateAll()
|
||||
},
|
||||
"player.durationchange": () => {
|
||||
this.calculateDuration()
|
||||
},
|
||||
}
|
||||
this.calculateDuration(
|
||||
manifest.metadata?.duration ?? manifest.duration,
|
||||
)
|
||||
},
|
||||
"player.seeked": (seek) => {
|
||||
this.calculateTime()
|
||||
this.updateAll()
|
||||
},
|
||||
"player.durationchange": () => {
|
||||
this.calculateDuration()
|
||||
},
|
||||
}
|
||||
|
||||
tick = () => {
|
||||
if (this.state.playing) {
|
||||
this.interval = setInterval(() => {
|
||||
this.updateAll()
|
||||
}, 1000)
|
||||
} else {
|
||||
if (this.interval) {
|
||||
clearInterval(this.interval)
|
||||
}
|
||||
}
|
||||
}
|
||||
tick = () => {
|
||||
if (this.state.playing) {
|
||||
this.interval = setInterval(() => {
|
||||
this.updateAll()
|
||||
}, 1000)
|
||||
} else {
|
||||
if (this.interval) {
|
||||
clearInterval(this.interval)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount = () => {
|
||||
this.calculateDuration()
|
||||
this.updateAll()
|
||||
this.tick()
|
||||
componentDidMount = () => {
|
||||
this.calculateDuration()
|
||||
this.updateAll()
|
||||
this.tick()
|
||||
|
||||
for (const [event, callback] of Object.entries(this.events)) {
|
||||
app.cores.player.eventBus().on(event, callback)
|
||||
}
|
||||
}
|
||||
for (const [event, callback] of Object.entries(this.events)) {
|
||||
app.cores.player.eventBus().on(event, callback)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount = () => {
|
||||
for (const [event, callback] of Object.entries(this.events)) {
|
||||
app.cores.player.eventBus().off(event, callback)
|
||||
}
|
||||
}
|
||||
componentWillUnmount = () => {
|
||||
for (const [event, callback] of Object.entries(this.events)) {
|
||||
app.cores.player.eventBus().off(event, callback)
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate = (prevProps, prevState) => {
|
||||
if (this.state.playing !== prevState.playing) {
|
||||
this.tick()
|
||||
}
|
||||
}
|
||||
componentDidUpdate = (prevProps, prevState) => {
|
||||
if (this.state.playing !== prevState.playing) {
|
||||
this.tick()
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div
|
||||
className={classnames(
|
||||
"player-seek_bar",
|
||||
{
|
||||
["stopped"]: this.props.stopped,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={classnames(
|
||||
"progress",
|
||||
{
|
||||
["hidden"]: this.props.streamMode,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<Slider
|
||||
size="small"
|
||||
value={this.state.sliderTime}
|
||||
disabled={this.props.stopped || this.props.streamMode || this.props.disabled}
|
||||
min={0}
|
||||
max={100}
|
||||
step={0.1}
|
||||
onChange={(_, value) => {
|
||||
this.setState({
|
||||
sliderTime: value,
|
||||
sliderLock: true
|
||||
})
|
||||
}}
|
||||
onChangeCommitted={() => {
|
||||
this.setState({
|
||||
sliderLock: false
|
||||
})
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
className={classnames("player-seek_bar", {
|
||||
["stopped"]: this.props.stopped,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={classnames("progress", {
|
||||
["hidden"]: this.props.streamMode,
|
||||
})}
|
||||
>
|
||||
<Slider
|
||||
size="small"
|
||||
value={this.state.sliderTime}
|
||||
disabled={
|
||||
this.props.stopped ||
|
||||
this.props.streamMode ||
|
||||
this.props.disabled
|
||||
}
|
||||
min={0}
|
||||
max={100}
|
||||
step={0.1}
|
||||
onChange={(_, value) => {
|
||||
this.setState({
|
||||
sliderTime: value,
|
||||
sliderLock: true,
|
||||
})
|
||||
}}
|
||||
onChangeCommitted={() => {
|
||||
this.setState({
|
||||
sliderLock: false,
|
||||
})
|
||||
|
||||
this.handleSeek(this.state.sliderTime)
|
||||
this.handleSeek(this.state.sliderTime)
|
||||
|
||||
if (!this.props.playing) {
|
||||
app.cores.player.playback.play()
|
||||
}
|
||||
}}
|
||||
valueLabelDisplay="auto"
|
||||
valueLabelFormat={(value) => {
|
||||
return seekToTimeLabel((value / 100) * app.cores.player.controls.duration())
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="timers">
|
||||
<div>
|
||||
<span>{this.state.timeText}</span>
|
||||
</div>
|
||||
<div>
|
||||
{
|
||||
this.props.streamMode ? <antd.Tag>Live</antd.Tag> : <span>{this.state.durationText}</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
if (!this.props.playing) {
|
||||
app.cores.player.playback.play()
|
||||
}
|
||||
}}
|
||||
valueLabelDisplay="auto"
|
||||
valueLabelFormat={(value) => {
|
||||
return seekToTimeLabel(
|
||||
(value / 100) *
|
||||
app.cores.player.controls.duration(),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{!this.props.streamMode && (
|
||||
<div className="timers">
|
||||
<div>
|
||||
<span>{this.state.timeText}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>{this.state.durationText}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import classnames from "classnames"
|
||||
|
||||
import { Icons } from "@components/Icons"
|
||||
import { usePlayerStateContext } from "@contexts/WithPlayerContext"
|
||||
import LiveInfo from "@components/Player/LiveInfo"
|
||||
import SeekBar from "@components/Player/SeekBar"
|
||||
import Controls from "@components/Player/Controls"
|
||||
|
||||
@ -25,9 +26,7 @@ function isOverflown(parent, element) {
|
||||
return elementRect.width > parentRect.width
|
||||
}
|
||||
|
||||
const Indicators = (props) => {
|
||||
const { track } = props
|
||||
|
||||
const Indicators = ({ track, playerState }) => {
|
||||
if (!track) {
|
||||
return null
|
||||
}
|
||||
@ -40,6 +39,12 @@ const Indicators = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (playerState.live) {
|
||||
indicators.push(
|
||||
<Icons.FiRadio style={{ color: "var(--colorPrimary)" }} />,
|
||||
)
|
||||
}
|
||||
|
||||
if (indicators.length === 0) {
|
||||
return null
|
||||
}
|
||||
@ -152,12 +157,6 @@ const Player = (props) => {
|
||||
onClick={() => app.location.push("/lyrics")}
|
||||
/>
|
||||
|
||||
{/* <antd.Button
|
||||
icon={<Icons.MdOfflineBolt />}
|
||||
>
|
||||
HyperDrive
|
||||
</antd.Button> */}
|
||||
|
||||
<antd.Button
|
||||
icon={<Icons.FiX />}
|
||||
shape="circle"
|
||||
@ -206,20 +205,26 @@ const Player = (props) => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{playerState.radioId && (
|
||||
<LiveInfo radioId={playerState.radioId} />
|
||||
)}
|
||||
|
||||
<div className="toolbar_player_actions">
|
||||
<Controls />
|
||||
<Controls streamMode={playerState.live} />
|
||||
|
||||
<SeekBar
|
||||
stopped={playerState.playback_status === "stopped"}
|
||||
playing={playerState.playback_status === "playing"}
|
||||
streamMode={playerState.livestream_mode}
|
||||
disabled={playerState.control_locked}
|
||||
streamMode={playerState.live}
|
||||
/>
|
||||
|
||||
<ExtraActions />
|
||||
<ExtraActions streamMode={playerState.live} />
|
||||
</div>
|
||||
|
||||
<Indicators track={playerState.track_manifest} />
|
||||
<Indicators
|
||||
track={playerState.track_manifest}
|
||||
playerState={playerState}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user