From 6f1fca02797eb726347acde5b0dc28652b77fbb0 Mon Sep 17 00:00:00 2001 From: SrGooglo Date: Wed, 14 Dec 2022 19:10:57 +0000 Subject: [PATCH] improve fech load --- packages/app/src/pages/live/[key].jsx | 223 ++++++++++++++++--------- packages/app/src/pages/live/index.less | 50 ++++-- 2 files changed, 182 insertions(+), 91 deletions(-) diff --git a/packages/app/src/pages/live/[key].jsx b/packages/app/src/pages/live/[key].jsx index dbcbe953..5d81bb33 100755 --- a/packages/app/src/pages/live/[key].jsx +++ b/packages/app/src/pages/live/[key].jsx @@ -1,6 +1,7 @@ import React from "react" import * as antd from "antd" +import classnames from "classnames" import Livestream from "models/livestream" import { FloatingPanel } from "antd-mobile" @@ -19,9 +20,12 @@ const floatingPanelAnchors = [160, 72 + 119, window.innerHeight * 0.8] export default class StreamViewer extends React.Component { state = { - isEnded: false, - sourceLoading: true, + requestedUsername: null, + isEnded: false, + loading: true, + + streamSources: null, streamInfo: null, spectators: 0, @@ -40,12 +44,14 @@ export default class StreamViewer extends React.Component { } attachDecoder = { - flv: (source) => { + flv: async (source) => { if (!source) { console.error("Stream source is not defined") return false } + this.toogleLoading(true) + const decoderInstance = mpegts.createPlayer({ type: "flv", isLive: true, @@ -53,9 +59,14 @@ export default class StreamViewer extends React.Component { url: source }) + decoderInstance.on(mpegts.Events.ERROR, this.onSourceEnd) + decoderInstance.attachMediaElement(this.videoPlayerRef.current) decoderInstance.load() - decoderInstance.play() + + await decoderInstance.play() + + this.toogleLoading(false) return decoderInstance }, @@ -102,8 +113,25 @@ export default class StreamViewer extends React.Component { } } + onSourceEnd = () => { + if (typeof this.state.decoderInstance?.destroy === "function") { + this.state.decoderInstance.destroy() + } + + this.state.player.destroy() + + this.setState({ + isEnded: true, + loading: false, + }) + } + loadStreamInfo = async (username) => { - const streamInfo = await Livestream.getLivestream({ username }) + const streamInfo = await Livestream.getStreamInfo({ username }).catch((error) => { + console.error(error) + + return null + }) if (!streamInfo) { return false @@ -113,44 +141,66 @@ export default class StreamViewer extends React.Component { this.setState({ streamInfo: streamInfo, - spectators: streamInfo.connectedClients, + }) + } + + loadStreamSources = async (username) => { + const streamSources = await Livestream.getLivestream({ username }).catch((error) => { + console.error(error) + + this.onSourceEnd(error) + + return null + }) + + if (!streamSources) { + return false + } + + console.log("Stream sources", streamSources) + + this.setState({ + streamSources: streamSources, + spectators: streamSources.connectedClients, }) } componentDidMount = async () => { + this.enterPlayerAnimation() + const requestedUsername = this.props.match.params.key - // get stream info - await this.loadStreamInfo(requestedUsername) + const player = new Plyr("#player", { + clickToPlay: false, + autoplay: true, + controls: ["mute", "volume", "fullscreen", "airplay", "options", "settings",], + settings: ["quality"], + ...this.state.plyrOptions, + }) - if (this.state.streamInfo) { - if (!this.state.streamInfo.sources) { + await this.setState({ + requestedUsername, + player, + }) + + // get stream info + this.loadStreamInfo(requestedUsername) + await this.loadStreamSources(requestedUsername) + + if (this.state.streamSources) { + if (!this.state.streamSources.sources) { console.error("Stream sources is not defined") return false } - this.enterPlayerAnimation() - - const player = new Plyr("#player", { - clickToPlay: false, - autoplay: true, - controls: ["mute", "volume", "fullscreen", "airplay", "options", "settings",], - settings: ["quality"], - ...this.state.plyrOptions, - }) - - await this.setState({ - player, - }) - - await this.loadDecoder("flv", this.state.streamInfo.sources.flv) + await this.loadDecoder("flv", this.state.streamSources.sources.flv) } // set a interval to update the stream info this.streamInfoInterval = setInterval(() => { - this.loadStreamInfo(requestedUsername) - }, 1000 * 60 * 3) + this.loadStreamSources(requestedUsername) + }, 1000 * 60) } componentWillUnmount = () => { @@ -203,8 +253,6 @@ export default class StreamViewer extends React.Component { this.setState({ decoderInstance: null }) } - this.toogleSourceLoading(true) - console.log(`Switching decoder to: ${decoder}`) const decoderInstance = await this.attachDecoder[decoder](...args) @@ -213,78 +261,93 @@ export default class StreamViewer extends React.Component { decoderInstance: decoderInstance }) - this.toogleSourceLoading(false) - return decoderInstance } - toogleSourceLoading = (to) => { - this.setState({ sourceLoading: to ?? !this.state.sourceLoading }) - } - - onSourceEnded = () => { - console.log("Source ended") - - this.setState({ isEnded: true }) + toogleLoading = (to) => { + this.setState({ loading: to ?? !this.state.loading }) } render() { - if (!this.state.streamInfo || this.state.isEnded) { - return
- -

- This stream is ended -

-
-
- } - return
-
- + { + this.state.streamInfo + ? <> +
+ -
- } - > - {this.state.spectators} - -
-
+ { + !this.state.isEnded &&
+ } + > + {this.state.spectators} + +
+ } +
-
-
-

{this.state.streamInfo?.info.title}

-
-
- - {({ index }) => { - return

{this.state.streamInfo?.info.description}

- }} -
-
-
+
+
+

{this.state.streamInfo?.title}

+
+
+ + {({ index }) => { + return

{this.state.streamInfo?.description}

+ }} +
+
+
+ + : + }
-
{ - window.isMobile ? - - - : -
+ window.isMobile + ? + + + :

Live chat

diff --git a/packages/app/src/pages/live/index.less b/packages/app/src/pages/live/index.less index 1bdddacc..9275e610 100755 --- a/packages/app/src/pages/live/index.less +++ b/packages/app/src/pages/live/index.less @@ -7,18 +7,9 @@ //justify-content: space-between; } -.stream_end { - position: absolute; - z-index: 100; - background-color: rgba(0, 0, 0, 0.8); - top: 0; - left: 0; - - width: 100%; - height: 100vh; -} - .livestream { + position: relative; + display: flex; flex-direction: row; @@ -46,7 +37,42 @@ display: flex; position: relative; + align-items: center; + justify-content: center; + width: calc(100% - @panel-width); + height: 100%; + + overflow-y: hidden; + + .livestream_player_loading { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + position: absolute; + z-index: 90; + + width: 100%; + height: 100%; + + backdrop-filter: blur(10px); + + transition: all 0.3s ease-in-out; + + opacity: 0; + + pointer-events: none; + + &.active { + opacity: 1; + } + } + + video { + z-index: 80; + } .livestream_player_header { display: flex; @@ -87,6 +113,8 @@ .livestream_player_header_user { display: flex; flex-direction: column; + + margin-right: 20px; .livestream_player_header_user_spectators { margin-top: 10px;