mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-11 03:24:16 +00:00
improve fech load
This commit is contained in:
parent
128f09a973
commit
6f1fca0279
@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
|
import classnames from "classnames"
|
||||||
|
|
||||||
import Livestream from "models/livestream"
|
import Livestream from "models/livestream"
|
||||||
import { FloatingPanel } from "antd-mobile"
|
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 {
|
export default class StreamViewer extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
isEnded: false,
|
requestedUsername: null,
|
||||||
sourceLoading: true,
|
|
||||||
|
|
||||||
|
isEnded: false,
|
||||||
|
loading: true,
|
||||||
|
|
||||||
|
streamSources: null,
|
||||||
streamInfo: null,
|
streamInfo: null,
|
||||||
spectators: 0,
|
spectators: 0,
|
||||||
|
|
||||||
@ -40,12 +44,14 @@ export default class StreamViewer extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
attachDecoder = {
|
attachDecoder = {
|
||||||
flv: (source) => {
|
flv: async (source) => {
|
||||||
if (!source) {
|
if (!source) {
|
||||||
console.error("Stream source is not defined")
|
console.error("Stream source is not defined")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.toogleLoading(true)
|
||||||
|
|
||||||
const decoderInstance = mpegts.createPlayer({
|
const decoderInstance = mpegts.createPlayer({
|
||||||
type: "flv",
|
type: "flv",
|
||||||
isLive: true,
|
isLive: true,
|
||||||
@ -53,9 +59,14 @@ export default class StreamViewer extends React.Component {
|
|||||||
url: source
|
url: source
|
||||||
})
|
})
|
||||||
|
|
||||||
|
decoderInstance.on(mpegts.Events.ERROR, this.onSourceEnd)
|
||||||
|
|
||||||
decoderInstance.attachMediaElement(this.videoPlayerRef.current)
|
decoderInstance.attachMediaElement(this.videoPlayerRef.current)
|
||||||
decoderInstance.load()
|
decoderInstance.load()
|
||||||
decoderInstance.play()
|
|
||||||
|
await decoderInstance.play()
|
||||||
|
|
||||||
|
this.toogleLoading(false)
|
||||||
|
|
||||||
return decoderInstance
|
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) => {
|
loadStreamInfo = async (username) => {
|
||||||
const streamInfo = await Livestream.getLivestream({ username })
|
const streamInfo = await Livestream.getStreamInfo({ username }).catch((error) => {
|
||||||
|
console.error(error)
|
||||||
|
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
if (!streamInfo) {
|
if (!streamInfo) {
|
||||||
return false
|
return false
|
||||||
@ -113,44 +141,66 @@ export default class StreamViewer extends React.Component {
|
|||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
streamInfo: streamInfo,
|
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 () => {
|
componentDidMount = async () => {
|
||||||
|
this.enterPlayerAnimation()
|
||||||
|
|
||||||
const requestedUsername = this.props.match.params.key
|
const requestedUsername = this.props.match.params.key
|
||||||
|
|
||||||
// get stream info
|
const player = new Plyr("#player", {
|
||||||
await this.loadStreamInfo(requestedUsername)
|
clickToPlay: false,
|
||||||
|
autoplay: true,
|
||||||
|
controls: ["mute", "volume", "fullscreen", "airplay", "options", "settings",],
|
||||||
|
settings: ["quality"],
|
||||||
|
...this.state.plyrOptions,
|
||||||
|
})
|
||||||
|
|
||||||
if (this.state.streamInfo) {
|
await this.setState({
|
||||||
if (!this.state.streamInfo.sources) {
|
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")
|
console.error("Stream sources is not defined")
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
this.enterPlayerAnimation()
|
await this.loadDecoder("flv", this.state.streamSources.sources.flv)
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set a interval to update the stream info
|
// set a interval to update the stream info
|
||||||
this.streamInfoInterval = setInterval(() => {
|
this.streamInfoInterval = setInterval(() => {
|
||||||
this.loadStreamInfo(requestedUsername)
|
this.loadStreamSources(requestedUsername)
|
||||||
}, 1000 * 60 * 3)
|
}, 1000 * 60)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount = () => {
|
componentWillUnmount = () => {
|
||||||
@ -203,8 +253,6 @@ export default class StreamViewer extends React.Component {
|
|||||||
this.setState({ decoderInstance: null })
|
this.setState({ decoderInstance: null })
|
||||||
}
|
}
|
||||||
|
|
||||||
this.toogleSourceLoading(true)
|
|
||||||
|
|
||||||
console.log(`Switching decoder to: ${decoder}`)
|
console.log(`Switching decoder to: ${decoder}`)
|
||||||
|
|
||||||
const decoderInstance = await this.attachDecoder[decoder](...args)
|
const decoderInstance = await this.attachDecoder[decoder](...args)
|
||||||
@ -213,78 +261,93 @@ export default class StreamViewer extends React.Component {
|
|||||||
decoderInstance: decoderInstance
|
decoderInstance: decoderInstance
|
||||||
})
|
})
|
||||||
|
|
||||||
this.toogleSourceLoading(false)
|
|
||||||
|
|
||||||
return decoderInstance
|
return decoderInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
toogleSourceLoading = (to) => {
|
toogleLoading = (to) => {
|
||||||
this.setState({ sourceLoading: to ?? !this.state.sourceLoading })
|
this.setState({ loading: to ?? !this.state.loading })
|
||||||
}
|
|
||||||
|
|
||||||
onSourceEnded = () => {
|
|
||||||
console.log("Source ended")
|
|
||||||
|
|
||||||
this.setState({ isEnded: true })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.state.streamInfo || this.state.isEnded) {
|
|
||||||
return <div className="stream_end">
|
|
||||||
<antd.Result>
|
|
||||||
<h1>
|
|
||||||
This stream is ended
|
|
||||||
</h1>
|
|
||||||
</antd.Result>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div className="livestream">
|
return <div className="livestream">
|
||||||
<div className="livestream_player">
|
<div className="livestream_player">
|
||||||
<div className="livestream_player_header">
|
<div className="livestream_player_header">
|
||||||
<div className="livestream_player_header_user">
|
{
|
||||||
<UserPreview username={this.state.streamInfo?.username} />
|
this.state.streamInfo
|
||||||
|
? <>
|
||||||
|
<div className="livestream_player_header_user">
|
||||||
|
<UserPreview username={this.state.requestedUsername} />
|
||||||
|
|
||||||
<div className="livestream_player_header_user_spectators">
|
{
|
||||||
<antd.Tag
|
!this.state.isEnded && <div className="livestream_player_header_user_spectators">
|
||||||
icon={<Icons.Eye />}
|
<antd.Tag
|
||||||
>
|
icon={<Icons.Eye />}
|
||||||
{this.state.spectators}
|
>
|
||||||
</antd.Tag>
|
{this.state.spectators}
|
||||||
</div>
|
</antd.Tag>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="livestream_player_header_info">
|
<div className="livestream_player_header_info">
|
||||||
<div className="livestream_player_header_info_title">
|
<div className="livestream_player_header_info_title">
|
||||||
<h1>{this.state.streamInfo?.info.title}</h1>
|
<h1>{this.state.streamInfo?.title}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="livestream_player_header_info_description">
|
<div className="livestream_player_header_info_description">
|
||||||
<Ticker
|
<Ticker
|
||||||
mode="smooth"
|
mode="smooth"
|
||||||
>
|
>
|
||||||
{({ index }) => {
|
{({ index }) => {
|
||||||
return <h4>{this.state.streamInfo?.info.description}</h4>
|
return <h4>{this.state.streamInfo?.description}</h4>
|
||||||
}}
|
}}
|
||||||
</Ticker>
|
</Ticker>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
: <antd.Skeleton active />
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<video ref={this.videoPlayerRef} id="player" />
|
<video
|
||||||
|
ref={this.videoPlayerRef}
|
||||||
|
id="player"
|
||||||
|
style={{
|
||||||
|
display: this.state.isEnded ? "none" : "block",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{
|
||||||
|
this.state.isEnded && <antd.Result>
|
||||||
|
<h1>
|
||||||
|
This stream is ended
|
||||||
|
</h1>
|
||||||
|
</antd.Result>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={classnames(
|
||||||
|
"livestream_player_loading",
|
||||||
|
{
|
||||||
|
["active"]: this.state.loading,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<antd.Spin />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
window.isMobile ?
|
window.isMobile
|
||||||
<FloatingPanel anchors={floatingPanelAnchors}>
|
? <FloatingPanel anchors={floatingPanelAnchors}>
|
||||||
<UserPreview username={this.state.streamInfo?.username} />
|
<UserPreview username={this.state.requestedUsername} />
|
||||||
</FloatingPanel> :
|
</FloatingPanel>
|
||||||
<div className="livestream_panel">
|
: <div className="livestream_panel">
|
||||||
<div className="chatbox">
|
<div className="chatbox">
|
||||||
<div className="chatbox_header">
|
<div className="chatbox_header">
|
||||||
<h4><Icons.MessageCircle /> Live chat</h4>
|
<h4><Icons.MessageCircle /> Live chat</h4>
|
||||||
</div>
|
</div>
|
||||||
<LiveChat
|
<LiveChat
|
||||||
roomId={`livestream/${this.state.streamInfo?.username}`}
|
roomId={`livestream:${this.state.requestedUsername}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,18 +7,9 @@
|
|||||||
//justify-content: space-between;
|
//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 {
|
.livestream {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
||||||
@ -46,7 +37,42 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
width: calc(100% - @panel-width);
|
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 {
|
.livestream_player_header {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -87,6 +113,8 @@
|
|||||||
.livestream_player_header_user {
|
.livestream_player_header_user {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
margin-right: 20px;
|
||||||
|
|
||||||
.livestream_player_header_user_spectators {
|
.livestream_player_header_user_spectators {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user