mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 02:54:15 +00:00
improve media player
This commit is contained in:
parent
7606d3c92c
commit
a89228171a
184
packages/app/src/components/BackgroundMediaPlayer/index.jsx
Normal file
184
packages/app/src/components/BackgroundMediaPlayer/index.jsx
Normal file
@ -0,0 +1,184 @@
|
||||
import React from "react"
|
||||
import Ticker from "react-ticker"
|
||||
import { FastAverageColor } from "fast-average-color"
|
||||
import classnames from "classnames"
|
||||
import { Icons } from "components/Icons"
|
||||
|
||||
import "./index.less"
|
||||
|
||||
const useEventBus = (events) => {
|
||||
const registerEvents = () => {
|
||||
for (const [event, handler] of Object.entries(events)) {
|
||||
app.eventBus.on(event, handler)
|
||||
}
|
||||
}
|
||||
|
||||
const unregisterEvents = () => {
|
||||
for (const [event, handler] of Object.entries(events)) {
|
||||
app.eventBus.off(event, handler)
|
||||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
registerEvents()
|
||||
|
||||
return () => {
|
||||
unregisterEvents()
|
||||
}
|
||||
}, [])
|
||||
}
|
||||
|
||||
const fac = new FastAverageColor()
|
||||
|
||||
const bruh = (props) => {
|
||||
const [thumbnailAnalysis, setThumbnailAnalysis] = React.useState("#000000")
|
||||
const [currentPlaying, setCurrentPlaying] = React.useState(app.cores.player.getState("currentAudioManifest"))
|
||||
const [plabackState, setPlaybackState] = React.useState(app.cores.player.getState("playbackStatus") ?? "stopped")
|
||||
|
||||
const onClickMinimize = () => {
|
||||
app.cores.player.minimize()
|
||||
}
|
||||
|
||||
const calculateAverageCoverColor = async () => {
|
||||
if (currentPlaying) {
|
||||
const color = await fac.getColorAsync(currentPlaying.thumbnail)
|
||||
setThumbnailAnalysis(color)
|
||||
updateBackgroundItem(color)
|
||||
}
|
||||
}
|
||||
|
||||
const updateBackgroundItem = () => {
|
||||
app.SidebarController.updateBackgroundItem(undefined, {
|
||||
icon: <Icons.MdMusicNote />,
|
||||
style: {
|
||||
backgroundColor: thumbnailAnalysis?.hex
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
useEventBus({
|
||||
"player.current.update": (data) => {
|
||||
console.log("player.current.update", data)
|
||||
setCurrentPlaying(data)
|
||||
updateBackgroundItem()
|
||||
},
|
||||
"player.playback.update": (data) => {
|
||||
setPlaybackState(data)
|
||||
updateBackgroundItem()
|
||||
}
|
||||
})
|
||||
|
||||
React.useEffect(() => {
|
||||
calculateAverageCoverColor()
|
||||
}, [currentPlaying])
|
||||
|
||||
React.useEffect(() => {
|
||||
|
||||
}, [])
|
||||
|
||||
return <div
|
||||
className="background_media_player"
|
||||
onClick={onClickMinimize}
|
||||
>
|
||||
{
|
||||
currentPlaying && <div
|
||||
className={classnames(
|
||||
"background_media_player__title",
|
||||
{
|
||||
["lightBackground"]: thumbnailAnalysis.isLight,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<h4>
|
||||
{currentPlaying.title} - {currentPlaying.artist}
|
||||
</h4>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
export default class BackgroundMediaPlayer extends React.Component {
|
||||
state = {
|
||||
thumbnailAnalysis: null,
|
||||
currentPlaying: app.cores.player.getState("currentAudioManifest"),
|
||||
plabackState: app.cores.player.getState("playbackStatus") ?? "stopped",
|
||||
}
|
||||
|
||||
events = {
|
||||
"player.current.update": (data) => {
|
||||
this.calculateAverageCoverColor()
|
||||
|
||||
this.setState({
|
||||
currentPlaying: data
|
||||
})
|
||||
},
|
||||
"player.playback.update": (data) => {
|
||||
this.updateBackgroundItem()
|
||||
|
||||
this.setState({
|
||||
plabackState: data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onClickMinimize = () => {
|
||||
app.cores.player.minimize()
|
||||
}
|
||||
|
||||
calculateAverageCoverColor = async () => {
|
||||
if (this.state.currentPlaying) {
|
||||
const color = await fac.getColorAsync(this.state.currentPlaying.thumbnail)
|
||||
|
||||
this.setState({
|
||||
thumbnailAnalysis: color
|
||||
})
|
||||
|
||||
this.updateBackgroundItem(color)
|
||||
}
|
||||
}
|
||||
|
||||
updateBackgroundItem = (analysis) => {
|
||||
app.SidebarController.updateBackgroundItem(undefined, {
|
||||
icon: <Icons.MdMusicNote />,
|
||||
style: {
|
||||
backgroundColor: analysis?.hex ?? this.state.thumbnailAnalysis?.hex,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
componentDidMount = async () => {
|
||||
this.calculateAverageCoverColor()
|
||||
|
||||
for (const [event, handler] of Object.entries(this.events)) {
|
||||
app.eventBus.on(event, handler)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
for (const [event, handler] of Object.entries(this.events)) {
|
||||
app.eventBus.off(event, handler)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div
|
||||
className="background_media_player"
|
||||
onClick={this.onClickMinimize}
|
||||
>
|
||||
{
|
||||
this.state.currentPlaying && <div
|
||||
className={classnames(
|
||||
"background_media_player__title",
|
||||
{
|
||||
["lightBackground"]: this.state.thumbnailAnalysis?.isLight,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<h4>
|
||||
{this.state.currentPlaying?.title} - {this.state.currentPlaying?.artist}
|
||||
</h4>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
36
packages/app/src/components/BackgroundMediaPlayer/index.less
Normal file
36
packages/app/src/components/BackgroundMediaPlayer/index.less
Normal file
@ -0,0 +1,36 @@
|
||||
.background_media_player {
|
||||
overflow: hidden;
|
||||
|
||||
width: 100%;
|
||||
|
||||
transition: all 150ms ease-in-out;
|
||||
|
||||
.background_media_player__title {
|
||||
overflow: hidden;
|
||||
|
||||
white-space: nowrap;
|
||||
|
||||
width: 100%;
|
||||
|
||||
&.lightBackground {
|
||||
h4 {
|
||||
color: var(--text-color-black);
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
font-family: "Space Grotesk", sans-serif;
|
||||
|
||||
color: var(--text-color-white);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ import React from "react"
|
||||
import * as antd from "antd"
|
||||
import Slider from "@mui/material/Slider"
|
||||
import classnames from "classnames"
|
||||
import Ticker from "react-ticker"
|
||||
|
||||
import { Icons, createIconRender } from "components/Icons"
|
||||
|
||||
@ -163,7 +164,7 @@ class SeekBar extends React.Component {
|
||||
|
||||
this.calculateTime()
|
||||
this.updateAll()
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
tick = () => {
|
||||
@ -273,6 +274,7 @@ export default class AudioPlayer extends React.Component {
|
||||
audioVolume: app.cores.player.getState("audioVolume") ?? 0.3,
|
||||
bpm: app.cores.player.getState("trackBPM") ?? 0,
|
||||
showControls: false,
|
||||
minimized: false,
|
||||
}
|
||||
|
||||
events = {
|
||||
@ -294,6 +296,13 @@ export default class AudioPlayer extends React.Component {
|
||||
"player.volume.update": (data) => {
|
||||
this.setState({ audioVolume: data })
|
||||
},
|
||||
"player.minimized.update": (minimized) => {
|
||||
console.log(`Player minimized updated: ${minimized}`)
|
||||
|
||||
this.setState({
|
||||
minimized
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount = async () => {
|
||||
@ -319,7 +328,7 @@ export default class AudioPlayer extends React.Component {
|
||||
}
|
||||
|
||||
minimize = () => {
|
||||
|
||||
app.cores.player.minimize()
|
||||
}
|
||||
|
||||
updateVolume = (value) => {
|
||||
@ -356,6 +365,7 @@ export default class AudioPlayer extends React.Component {
|
||||
"embbededMediaPlayerWrapper",
|
||||
{
|
||||
["hovering"]: this.state.showControls,
|
||||
["minimized"]: this.state.minimized,
|
||||
}
|
||||
)}
|
||||
onMouseEnter={this.onMouse}
|
||||
|
@ -18,6 +18,15 @@
|
||||
|
||||
border-radius: 12px;
|
||||
|
||||
pointer-events: initial;
|
||||
|
||||
transition: all 150ms ease-in-out;
|
||||
|
||||
&.minimized {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&.hovering {
|
||||
.actions_wrapper {
|
||||
.actions {
|
||||
@ -88,7 +97,7 @@
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
|
||||
opacity: 0;
|
||||
|
||||
transition: all 150ms ease-in-out;
|
||||
@ -97,7 +106,7 @@
|
||||
|
||||
.ant-btn {
|
||||
background-color: var(--background-color-accent);
|
||||
|
||||
|
||||
svg {
|
||||
margin: 0 !important;
|
||||
}
|
||||
@ -231,6 +240,7 @@
|
||||
|
||||
.muteButton {
|
||||
padding: 10px;
|
||||
|
||||
svg {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
@ -119,6 +119,34 @@ export default class Sidebar extends React.Component {
|
||||
isExpanded: () => this.state.expanded,
|
||||
setCustomRender: this.setRender,
|
||||
closeCustomRender: this.closeRender,
|
||||
updateBackgroundItem: (children, props) => {
|
||||
let updatedValue = this.state.backgroundItem
|
||||
|
||||
if (typeof children !== "undefined") {
|
||||
updatedValue.children = children
|
||||
}
|
||||
|
||||
if (typeof props !== "undefined") {
|
||||
updatedValue.props = props
|
||||
}
|
||||
|
||||
this.setState({
|
||||
backgroundItem: updatedValue
|
||||
})
|
||||
},
|
||||
setBackgroundItem: (children, props) => {
|
||||
this.setState({
|
||||
backgroundItem: {
|
||||
children: children,
|
||||
props: props,
|
||||
},
|
||||
})
|
||||
},
|
||||
clearBackgroundItem: () => {
|
||||
this.setState({
|
||||
backgroundItem: null,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.state = {
|
||||
@ -130,6 +158,7 @@ export default class Sidebar extends React.Component {
|
||||
|
||||
customRenderTitle: null,
|
||||
customRender: null,
|
||||
backgroundItem: null,
|
||||
}
|
||||
|
||||
// handle sidedrawer open/close
|
||||
@ -228,6 +257,10 @@ export default class Sidebar extends React.Component {
|
||||
}
|
||||
|
||||
handleClick = (e) => {
|
||||
if (e.item.props.ignoreClick) {
|
||||
return
|
||||
}
|
||||
|
||||
if (e.item.props.override_event) {
|
||||
return app.eventBus.emit(e.item.props.override_event, e.item.props.override_event_props)
|
||||
}
|
||||
@ -341,6 +374,16 @@ export default class Sidebar extends React.Component {
|
||||
|
||||
<div key="bottom" className={classnames("app_sidebar_menu_wrapper", "bottom")}>
|
||||
<Menu selectable={false} mode="inline" onClick={this.handleClick}>
|
||||
<Menu.Item
|
||||
{...this.state.backgroundItem?.props ?? {}}
|
||||
key="background_item"
|
||||
className={classnames("background_item", { ["active"]: this.state.backgroundItem })}
|
||||
ignoreClick
|
||||
>
|
||||
{
|
||||
this.state.backgroundItem?.children
|
||||
}
|
||||
</Menu.Item>
|
||||
<Menu.Item key="search" icon={<Icons.Search />} >
|
||||
<Translation>
|
||||
{(t) => t("Search")}
|
||||
|
@ -182,6 +182,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
.background_item {
|
||||
width: 100%;
|
||||
|
||||
opacity: 0;
|
||||
|
||||
transition: all 150ms ease-in-out;
|
||||
|
||||
padding-bottom: 10px;
|
||||
|
||||
background-color: transparent;
|
||||
|
||||
&:hover{
|
||||
background-color: unset!important;
|
||||
}
|
||||
|
||||
&:active{
|
||||
background-color: unset!important;
|
||||
}
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.render_content_wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -1,9 +1,12 @@
|
||||
import React from "react"
|
||||
import Core from "evite/src/core"
|
||||
import { Observable } from "object-observer"
|
||||
import store from "store"
|
||||
// import { createRealTimeBpmProcessor } from "realtime-bpm-analyzer"
|
||||
|
||||
import { EmbbededMediaPlayer } from "components"
|
||||
import EmbbededMediaPlayer from "components/EmbbededMediaPlayer"
|
||||
import BackgroundMediaPlayer from "components/BackgroundMediaPlayer"
|
||||
|
||||
import { DOMWindow } from "components/RenderWindow"
|
||||
|
||||
class AudioPlayerStorage {
|
||||
@ -47,6 +50,7 @@ export default class Player extends Core {
|
||||
|
||||
state = Observable.from({
|
||||
loading: false,
|
||||
minimized: false,
|
||||
audioMuted: AudioPlayerStorage.get("mute") ?? false,
|
||||
playbackMode: AudioPlayerStorage.get("mode") ?? "repeat",
|
||||
audioVolume: AudioPlayerStorage.get("volume") ?? 0.3,
|
||||
@ -63,6 +67,7 @@ export default class Player extends Core {
|
||||
attachPlayerComponent: this.attachPlayerComponent.bind(this),
|
||||
detachPlayerComponent: this.detachPlayerComponent.bind(this),
|
||||
toogleMute: this.toogleMute.bind(this),
|
||||
minimize: this.toogleMinimize.bind(this),
|
||||
volume: this.volume.bind(this),
|
||||
start: this.start.bind(this),
|
||||
startPlaylist: this.startPlaylist.bind(this),
|
||||
@ -198,6 +203,17 @@ export default class Player extends Core {
|
||||
case "playbackStatus": {
|
||||
app.eventBus.emit("player.status.update", change.object.playbackStatus)
|
||||
|
||||
break
|
||||
}
|
||||
case "minimized": {
|
||||
if (change.object.minimized) {
|
||||
app.SidebarController.setBackgroundItem(React.createElement(BackgroundMediaPlayer))
|
||||
} else {
|
||||
app.SidebarController.setBackgroundItem(null)
|
||||
}
|
||||
|
||||
app.eventBus.emit("player.minimized.update", change.object.minimized)
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -633,6 +649,12 @@ export default class Player extends Core {
|
||||
return this.state.audioMuted
|
||||
}
|
||||
|
||||
toogleMinimize(to) {
|
||||
this.state.minimized = to ?? !this.state.minimized
|
||||
|
||||
return this.state.minimized
|
||||
}
|
||||
|
||||
volume(volume) {
|
||||
if (typeof volume !== "number") {
|
||||
return this.state.audioVolume
|
||||
@ -716,4 +738,15 @@ export default class Player extends Core {
|
||||
|
||||
return this.state.velocity
|
||||
}
|
||||
|
||||
collapse(to) {
|
||||
if (typeof to !== "boolean") {
|
||||
console.warn("Collapse must be a boolean")
|
||||
return false
|
||||
}
|
||||
|
||||
this.state.collapsed = to ?? !this.state.collapsed
|
||||
|
||||
return this.state.collapsed
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user