From a89228171a03258ebb1468b04ef7381af75ab446 Mon Sep 17 00:00:00 2001 From: SrGooglo Date: Tue, 14 Mar 2023 19:52:30 +0000 Subject: [PATCH] improve media player --- .../BackgroundMediaPlayer/index.jsx | 184 ++++++++++++++++++ .../BackgroundMediaPlayer/index.less | 36 ++++ .../components/EmbbededMediaPlayer/index.jsx | 14 +- .../components/EmbbededMediaPlayer/index.less | 14 +- .../src/components/Layout/sidebar/index.jsx | 43 ++++ .../src/components/Layout/sidebar/index.less | 24 +++ packages/app/src/cores/player/index.jsx | 35 +++- 7 files changed, 345 insertions(+), 5 deletions(-) create mode 100644 packages/app/src/components/BackgroundMediaPlayer/index.jsx create mode 100644 packages/app/src/components/BackgroundMediaPlayer/index.less diff --git a/packages/app/src/components/BackgroundMediaPlayer/index.jsx b/packages/app/src/components/BackgroundMediaPlayer/index.jsx new file mode 100644 index 00000000..b237af17 --- /dev/null +++ b/packages/app/src/components/BackgroundMediaPlayer/index.jsx @@ -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: , + 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
+ { + currentPlaying &&
+

+ {currentPlaying.title} - {currentPlaying.artist} +

+
+ } +
+} + +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: , + 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
+ { + this.state.currentPlaying &&
+

+ {this.state.currentPlaying?.title} - {this.state.currentPlaying?.artist} +

+
+ } +
+ } +} \ No newline at end of file diff --git a/packages/app/src/components/BackgroundMediaPlayer/index.less b/packages/app/src/components/BackgroundMediaPlayer/index.less new file mode 100644 index 00000000..cca0649c --- /dev/null +++ b/packages/app/src/components/BackgroundMediaPlayer/index.less @@ -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); + } + + } +} \ No newline at end of file diff --git a/packages/app/src/components/EmbbededMediaPlayer/index.jsx b/packages/app/src/components/EmbbededMediaPlayer/index.jsx index 43df5f9e..55e8695a 100755 --- a/packages/app/src/components/EmbbededMediaPlayer/index.jsx +++ b/packages/app/src/components/EmbbededMediaPlayer/index.jsx @@ -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} diff --git a/packages/app/src/components/EmbbededMediaPlayer/index.less b/packages/app/src/components/EmbbededMediaPlayer/index.less index 8055dba9..975a2f31 100755 --- a/packages/app/src/components/EmbbededMediaPlayer/index.less +++ b/packages/app/src/components/EmbbededMediaPlayer/index.less @@ -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; } diff --git a/packages/app/src/components/Layout/sidebar/index.jsx b/packages/app/src/components/Layout/sidebar/index.jsx index b90c218c..fff64309 100755 --- a/packages/app/src/components/Layout/sidebar/index.jsx +++ b/packages/app/src/components/Layout/sidebar/index.jsx @@ -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 {
+ + { + this.state.backgroundItem?.children + } + } > {(t) => t("Search")} diff --git a/packages/app/src/components/Layout/sidebar/index.less b/packages/app/src/components/Layout/sidebar/index.less index f1663dae..5755d354 100755 --- a/packages/app/src/components/Layout/sidebar/index.less +++ b/packages/app/src/components/Layout/sidebar/index.less @@ -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; diff --git a/packages/app/src/cores/player/index.jsx b/packages/app/src/cores/player/index.jsx index 5c08331d..499e2a22 100755 --- a/packages/app/src/cores/player/index.jsx +++ b/packages/app/src/cores/player/index.jsx @@ -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 + } } \ No newline at end of file