import React from "react" import * as antd from "antd" import Slider from "@mui/material/Slider" import classnames from "classnames" import UseAnimations from "react-useanimations" import LoadingAnimation from "react-useanimations/lib/loading" import LikeButton from "components/LikeButton" import { Icons, createIconRender } from "components/Icons" import "./index.less" // TODO: Queue view const AudioVolume = (props) => { return
{ return `${Math.round(value * 100)}%` } }} vertical />
} export class SeekBar extends React.Component { state = { 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.duration() // calculate the seek of the audio const seek = (value / 100) * duration app.cores.player.seek(seek) } else { app.cores.player.seek(0) } } calculateDuration = () => { // get current audio duration const audioDuration = app.cores.player.duration() if (isNaN(audioDuration)) { return } console.log(`Audio duration: ${audioDuration}`) // set duration this.setState({ durationText: this.seekToTimeLabel(audioDuration) }) } calculateTime = () => { // get current audio seek const seek = app.cores.player.seek() // set time this.setState({ timeText: this.seekToTimeLabel(seek) }) } seekToTimeLabel = (value) => { // convert seek to minutes and seconds const minutes = Math.floor(value / 60) // add leading zero if minutes is less than 10 const minutesString = minutes < 10 ? `0${minutes}` : minutes // get seconds const seconds = Math.floor(value - minutes * 60) // add leading zero if seconds is less than 10 const secondsString = seconds < 10 ? `0${seconds}` : seconds return `${minutesString}:${secondsString}` } updateProgressBar = () => { if (this.state.sliderLock) { return } const seek = app.cores.player.seek() const duration = app.cores.player.duration() const percent = (seek / duration) * 100 this.setState({ sliderTime: percent }) } updateAll = () => { this.calculateTime() this.updateProgressBar() } events = { "player.status.update": (status) => { console.log(`Player status updated: ${status}`) switch (status) { case "stopped": this.setState({ timeText: "00:00", durationText: "00:00", sliderTime: 0, }) break case "playing": this.updateAll() this.calculateDuration() break default: break } }, "player.current.update": (currentAudioManifest) => { console.log(`Player current audio updated:`, currentAudioManifest) this.updateAll() this.setState({ timeText: "00:00", sliderTime: 0, }) this.calculateDuration() }, "player.duration.update": (duration) => { console.log(`Player duration updated: ${duration}`) this.calculateDuration() }, "player.seek.update": (seek) => { console.log(`Player seek updated: ${seek}`) this.calculateTime() this.updateAll() }, } tick = () => { if (this.props.playing || this.props.streamMode) { this.interval = setInterval(() => { this.updateAll() }, 1000) } else { if (this.interval) { clearInterval(this.interval) } } } componentDidMount = () => { this.calculateDuration() this.tick() for (const [event, callback] of Object.entries(this.events)) { app.eventBus.on(event, callback) } } componentWillUnmount = () => { for (const [event, callback] of Object.entries(this.events)) { app.eventBus.off(event, callback) } } componentDidUpdate = (prevProps, prevState) => { if (this.props.playing !== prevProps.playing) { this.tick() } } render() { return
{ this.setState({ sliderTime: value, sliderLock: true }) }} onChangeCommitted={() => { this.setState({ sliderLock: false }) this.handleSeek(this.state.sliderTime) if (!this.props.playing) { app.cores.player.playback.play() } }} valueLabelDisplay="auto" valueLabelFormat={(value) => { return this.seekToTimeLabel((value / 100) * app.cores.player.duration()) }} />
{this.state.timeText}
{ this.props.streamMode ? Live : {this.state.durationText} }
} } const AudioPlayerChangeModeButton = (props) => { const [mode, setMode] = React.useState(app.cores.player.playback.mode()) const modeToIcon = { "normal": "MdArrowForward", "repeat": "MdRepeat", "shuffle": "MdShuffle", } const onClick = () => { const modes = Object.keys(modeToIcon) const newMode = modes[(modes.indexOf(mode) + 1) % modes.length] app.cores.player.playback.mode(newMode) setMode(newMode) } return } export default class AudioPlayer extends React.Component { state = { loading: app.cores.player.getState("loading") ?? false, currentPlaying: app.cores.player.getState("currentAudioManifest"), playbackStatus: app.cores.player.getState("playbackStatus") ?? "stopped", audioMuted: app.cores.player.getState("audioMuted") ?? false, audioVolume: app.cores.player.getState("audioVolume") ?? 0.3, bpm: app.cores.player.getState("trackBPM") ?? 0, showControls: false, minimized: false, streamMode: false, syncModeLocked: app.cores.player.getState("syncModeLocked"), syncMode: app.cores.player.getState("syncMode"), } events = { "player.syncModeLocked.update": (to) => { this.setState({ syncModeLocked: to }) }, "player.syncMode.update": (to) => { this.setState({ syncMode: to }) }, "player.livestream.update": (data) => { this.setState({ streamMode: data }) }, "player.bpm.update": (data) => { this.setState({ bpm: data }) }, "player.loading.update": (data) => { this.setState({ loading: data }) }, "player.status.update": (data) => { this.setState({ playbackStatus: data }) }, "player.current.update": (data) => { this.setState({ currentPlaying: data }) }, "player.mute.update": (data) => { this.setState({ audioMuted: data }) }, "player.volume.update": (data) => { this.setState({ audioVolume: data }) }, "player.minimized.update": (minimized) => { this.setState({ minimized }) } } componentDidMount = async () => { Object.entries(this.events).forEach(([event, callback]) => { app.eventBus.on(event, callback) }) } componentWillUnmount() { Object.entries(this.events).forEach(([event, callback]) => { app.eventBus.off(event, callback) }) } onMouse = (event) => { const { type } = event if (type === "mouseenter") { this.setState({ showControls: true }) } else if (type === "mouseleave") { this.setState({ showControls: false }) } } minimize = () => { app.cores.player.minimize() } close = () => { app.cores.player.close() } openVisualizer = () => { app.setLocation("/lyrics") } inviteSync = () => { app.cores.sync.music.createSyncRoom() } updateVolume = (value) => { app.cores.player.volume(value) } toogleMute = () => { app.cores.player.toogleMute() } onClickPlayButton = () => { if (this.state.streamMode) { return app.cores.player.playback.stop() } app.cores.player.playback.toogle() } onClickPreviousButton = () => { app.cores.player.playback.previous() } onClickNextButton = () => { app.cores.player.playback.next() } onClickLikeButton = () => { // TODO: Like console.log("Like") this.setState({ liked: !this.state.liked }) } render() { const { loading, currentPlaying, playbackStatus, audioMuted, audioVolume, } = this.state return
} onClick={this.minimize} shape="circle" /> { !this.state.syncModeLocked && !this.state.syncMode && } onClick={this.inviteSync} shape="circle" /> } } onClick={this.openVisualizer} shape="circle" /> } onClick={this.close} shape="square" />

{ currentPlaying?.title ? currentPlaying?.title : (loading ? "Loading..." : (currentPlaying?.title ?? "Untitled")) }

{ currentPlaying?.artist &&

{currentPlaying?.artist ?? "Unknown"}

}
} onClick={this.onClickPreviousButton} disabled={this.state.syncModeLocked} /> : playbackStatus === "playing" ? : } onClick={this.onClickPlayButton} className="playButton" disabled={this.state.syncModeLocked} > { loading &&
}
} onClick={this.onClickNextButton} disabled={this.state.syncModeLocked} />
{ audioMuted ? : }
} }