mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 02:54:15 +00:00
update StreamViewer
This commit is contained in:
parent
ec249b70db
commit
b976a595df
@ -1,15 +1,25 @@
|
|||||||
import React from 'react'
|
import React from "react"
|
||||||
|
import config from "config"
|
||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
import Plyr from 'plyr'
|
import { Icons } from "components/Icons"
|
||||||
import Hls from 'hls.js'
|
import moment from "moment"
|
||||||
import mpegts from 'mpegts.js'
|
|
||||||
|
import Plyr from "plyr"
|
||||||
|
import Hls from "hls.js"
|
||||||
|
import mpegts from "mpegts.js"
|
||||||
|
|
||||||
import "plyr/dist/plyr.css"
|
import "plyr/dist/plyr.css"
|
||||||
|
import "./index.less"
|
||||||
|
|
||||||
const streamsSource = "http://media.ragestudio.net/live"
|
const streamsSource = config.remotes.streamingApi
|
||||||
|
|
||||||
export default class StreamViewer extends React.Component {
|
export default class StreamViewer extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
|
userData: null,
|
||||||
|
streamInfo: null,
|
||||||
|
spectators: 0,
|
||||||
|
timeFromNow: "00:00:00",
|
||||||
|
|
||||||
player: null,
|
player: null,
|
||||||
streamKey: null,
|
streamKey: null,
|
||||||
streamSource: null,
|
streamSource: null,
|
||||||
@ -22,18 +32,98 @@ export default class StreamViewer extends React.Component {
|
|||||||
|
|
||||||
componentDidMount = async () => {
|
componentDidMount = async () => {
|
||||||
const query = new URLSearchParams(window.location.search)
|
const query = new URLSearchParams(window.location.search)
|
||||||
const requested = query.get("key")
|
const requestedUsername = query.get("key")
|
||||||
|
|
||||||
const source = `${streamsSource}/${requested}`
|
const source = `${streamsSource}/${requestedUsername}`
|
||||||
const player = new Plyr('#player')
|
const player = new Plyr("#player", {
|
||||||
|
autoplay: true,
|
||||||
|
controls: ["play", "mute", "volume", "fullscreen", "options", "settings"],
|
||||||
|
})
|
||||||
|
|
||||||
await this.setState({
|
await this.setState({
|
||||||
player,
|
player,
|
||||||
streamKey: requested,
|
streamKey: requestedUsername,
|
||||||
streamSource: source,
|
streamSource: source,
|
||||||
})
|
})
|
||||||
|
|
||||||
await this.loadWithProtocol[this.state.loadedProtocol]()
|
await this.loadWithProtocol[this.state.loadedProtocol]()
|
||||||
|
|
||||||
|
// make the interface a bit confortable for a video player
|
||||||
|
app.ThemeController.applyVariant("dark")
|
||||||
|
app.eventBus.emit("toogleCompactMode", true)
|
||||||
|
app.SidebarController.toogleVisible(false)
|
||||||
|
app.HeaderController.toogleVisible(false)
|
||||||
|
|
||||||
|
// fetch user info in the background
|
||||||
|
this.gatherUserInfo()
|
||||||
|
|
||||||
|
// fetch stream info in the background
|
||||||
|
// await for it
|
||||||
|
await this.gatherStreamInfo()
|
||||||
|
|
||||||
|
// create timer
|
||||||
|
if (this.state.streamInfo.connectCreated) {
|
||||||
|
this.createTimerCounter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount = () => {
|
||||||
|
app.ThemeController.applyVariant(app.settings.get("themeVariant"))
|
||||||
|
app.eventBus.emit("toogleCompactMode", false)
|
||||||
|
app.SidebarController.toogleVisible(true)
|
||||||
|
app.HeaderController.toogleVisible(true)
|
||||||
|
app.HeaderController.toogleVisible(true)
|
||||||
|
|
||||||
|
if (this.timerCounterInterval) {
|
||||||
|
this.timerCounterInterval = clearInterval(this.timerCounterInterval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gatherStreamInfo = async () => {
|
||||||
|
const result = await app.request.get.streamInfoFromUsername(undefined, {
|
||||||
|
username: this.state.streamKey,
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error(error)
|
||||||
|
antd.message.error(error.message)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
this.setState({
|
||||||
|
streamInfo: result,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gatherUserInfo = async () => {
|
||||||
|
const result = await app.request.get.user(undefined, {
|
||||||
|
username: this.state.streamKey,
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error(error)
|
||||||
|
antd.message.error(error.message)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
this.setState({
|
||||||
|
userData: result,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createTimerCounter = () => {
|
||||||
|
this.timerCounterInterval = setInterval(() => {
|
||||||
|
const secondsFromNow = moment().diff(moment(this.state.streamInfo.connectCreated), "seconds")
|
||||||
|
|
||||||
|
// calculate hours minutes and seconds
|
||||||
|
const hours = Math.floor(secondsFromNow / 3600)
|
||||||
|
const minutes = Math.floor((secondsFromNow - hours * 3600) / 60)
|
||||||
|
const seconds = secondsFromNow - hours * 3600 - minutes * 60
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
timeFromNow: `${hours}:${minutes}:${seconds}`,
|
||||||
|
})
|
||||||
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateQuality = (newQuality) => {
|
updateQuality = (newQuality) => {
|
||||||
@ -60,10 +150,10 @@ export default class StreamViewer extends React.Component {
|
|||||||
console.log("Switching to " + protocol)
|
console.log("Switching to " + protocol)
|
||||||
this.loadWithProtocol[protocol]()
|
this.loadWithProtocol[protocol]()
|
||||||
}
|
}
|
||||||
|
|
||||||
loadWithProtocol = {
|
loadWithProtocol = {
|
||||||
hls: () => {
|
hls: () => {
|
||||||
const source = `${this.state.streamSource}.m3u8`
|
const source = `${streamsSource}/stream/hls/${this.state.streamKey}`
|
||||||
const hls = new Hls()
|
const hls = new Hls()
|
||||||
|
|
||||||
hls.loadSource(source)
|
hls.loadSource(source)
|
||||||
@ -72,9 +162,13 @@ export default class StreamViewer extends React.Component {
|
|||||||
this.setState({ protocolInstance: hls, loadedProtocol: "hls" })
|
this.setState({ protocolInstance: hls, loadedProtocol: "hls" })
|
||||||
},
|
},
|
||||||
flv: () => {
|
flv: () => {
|
||||||
const source = `${this.state.streamSource}.flv`
|
const source = `${streamsSource}/stream/flv/${this.state.streamKey}`
|
||||||
|
|
||||||
const instance = mpegts.createPlayer({ type: 'flv', url: source, isLive: true })
|
const instance = mpegts.createPlayer({
|
||||||
|
type: "flv",
|
||||||
|
url: source,
|
||||||
|
isLive: true
|
||||||
|
})
|
||||||
|
|
||||||
instance.attachMediaElement(this.videoPlayerRef.current)
|
instance.attachMediaElement(this.videoPlayerRef.current)
|
||||||
instance.load()
|
instance.load()
|
||||||
@ -85,15 +179,39 @@ export default class StreamViewer extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <div>
|
return <div className="stream">
|
||||||
<antd.Select
|
|
||||||
onChange={(value) => this.switchProtocol(value)}
|
|
||||||
value={this.state.loadedProtocol}
|
|
||||||
>
|
|
||||||
<antd.Select.Option value="hls">HLS</antd.Select.Option>
|
|
||||||
<antd.Select.Option value="flv">FLV</antd.Select.Option>
|
|
||||||
</antd.Select>
|
|
||||||
<video ref={this.videoPlayerRef} id="player" />
|
<video ref={this.videoPlayerRef} id="player" />
|
||||||
|
<div className="panel">
|
||||||
|
<div className="info">
|
||||||
|
<div className="title">
|
||||||
|
<div>
|
||||||
|
<antd.Avatar
|
||||||
|
shape="square"
|
||||||
|
src={this.state.userData?.avatar}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>{this.state.userData?.username}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="spectatorCount">
|
||||||
|
<Icons.Eye />
|
||||||
|
{this.state.spectators}
|
||||||
|
</div>
|
||||||
|
<div id="timeCount">
|
||||||
|
<Icons.Clock />
|
||||||
|
{this.state.timeFromNow}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="chatbox">
|
||||||
|
{/* TODO: Use chatbox component and join to stream channel using username */}
|
||||||
|
<antd.Result>
|
||||||
|
<h1>
|
||||||
|
Cannot connect with chat server
|
||||||
|
</h1>
|
||||||
|
</antd.Result>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
92
packages/app/src/pages/streams/viewer/index.less
Normal file
92
packages/app/src/pages/streams/viewer/index.less
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
.plyr__controls {
|
||||||
|
width: 100%;
|
||||||
|
display: inline-flex;
|
||||||
|
//justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stream {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
|
||||||
|
color: var(--background-color-contrast);
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
span,
|
||||||
|
p {
|
||||||
|
color: var(--background-color-contrast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
height: 100vh;
|
||||||
|
width: 20vw;
|
||||||
|
|
||||||
|
.info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 10vh;
|
||||||
|
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
backdrop-filter: 20px;
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
>div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
height: fit-content;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
>div {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chatbox {
|
||||||
|
width: 20vw;
|
||||||
|
padding: 20px;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#spectatorCount {
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#timeCount {
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.plyr {
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
width: 80vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user