From bdd85850e2c22782971441ca66bbf4b66c05f0f2 Mon Sep 17 00:00:00 2001 From: SrGooglo Date: Thu, 6 Mar 2025 04:02:07 +0000 Subject: [PATCH] use comtyjs model --- packages/app/src/pages/tv/live/[id].jsx | 632 ++++++++++++------------ 1 file changed, 326 insertions(+), 306 deletions(-) diff --git a/packages/app/src/pages/tv/live/[id].jsx b/packages/app/src/pages/tv/live/[id].jsx index 73b5b419..c2b02250 100755 --- a/packages/app/src/pages/tv/live/[id].jsx +++ b/packages/app/src/pages/tv/live/[id].jsx @@ -7,7 +7,7 @@ import UserPreview from "@components/UserPreview" import { Icons } from "@components/Icons" import LiveChat from "@components/LiveChat" -import SpectrumAPI from "@classes/SpectrumAPI" +import SpectrumModel from "@models/spectrum" import Plyr from "plyr" import Hls from "hls.js" @@ -17,386 +17,406 @@ import "plyr/dist/plyr.css" import "./index.less" const DecodersEvents = { - [Hls.Events.FPS_DROP]: (event, data) => { - console.log("FPS_DROP Detected", data) - }, + [Hls.Events.FPS_DROP]: (event, data) => { + console.log("FPS_DROP Detected", data) + }, } const StreamDecoders = { - flv: async (player, source, { onSourceEnd } = {}) => { - if (!source) { - console.error("Stream source is not defined") - return false - } + flv: async (player, source, { onSourceEnd } = {}) => { + if (!source) { + console.error("Stream source is not defined") + return false + } - const decoderInstance = mpegts.createPlayer({ - type: "flv", - isLive: true, - enableWorker: true, - url: source - }) + const decoderInstance = mpegts.createPlayer({ + type: "flv", + isLive: true, + enableWorker: true, + url: source, + }) - if (typeof onSourceEnd === "function") { - decoderInstance.on(mpegts.Events.ERROR, onSourceEnd) - } + if (typeof onSourceEnd === "function") { + decoderInstance.on(mpegts.Events.ERROR, onSourceEnd) + } - decoderInstance.attachMediaElement(player) + decoderInstance.attachMediaElement(player) - decoderInstance.load() + decoderInstance.load() - await decoderInstance.play().catch((error) => { - console.error(error) - }) + await decoderInstance.play().catch((error) => { + console.error(error) + }) - return decoderInstance - }, - hls: (player, source) => { - if (!source) { - console.error("Stream source is not defined") - return false - } + return decoderInstance + }, + hls: (player, source) => { + if (!source) { + console.error("Stream source is not defined") + return false + } - const hlsInstance = new Hls({ - autoStartLoad: true, - }) + const hlsInstance = new Hls({ + autoStartLoad: true, + }) - hlsInstance.attachMedia(player.current) + hlsInstance.attachMedia(player.current) - hlsInstance.on(Hls.Events.MEDIA_ATTACHED, () => { - hlsInstance.loadSource(source) + hlsInstance.on(Hls.Events.MEDIA_ATTACHED, () => { + hlsInstance.loadSource(source) - hlsInstance.on(Hls.Events.MANIFEST_PARSED, (event, data) => { - console.log(`${data.levels.length} quality levels found`) - }) - }) + hlsInstance.on(Hls.Events.MANIFEST_PARSED, (event, data) => { + console.log(`${data.levels.length} quality levels found`) + }) + }) - hlsInstance.on(Hls.Events.ERROR, (event, data) => { - console.error(event, data) + hlsInstance.on(Hls.Events.ERROR, (event, data) => { + console.error(event, data) - switch (data.details) { - case Hls.ErrorDetails.FRAG_LOAD_ERROR: { - console.error(`Error loading fragment ${data.frag.url}`) - return - } - default: { - return - } - } - }) + switch (data.details) { + case Hls.ErrorDetails.FRAG_LOAD_ERROR: { + console.error(`Error loading fragment ${data.frag.url}`) + return + } + default: { + return + } + } + }) - // register player decoder events - Object.keys(DecodersEvents).forEach((event) => { - hlsInstance.on(event, DecodersEvents[event]) - }) + // register player decoder events + Object.keys(DecodersEvents).forEach((event) => { + hlsInstance.on(event, DecodersEvents[event]) + }) - return hlsInstance - } + return hlsInstance + }, } export default class StreamViewer extends React.Component { - state = { - isEnded: false, - loading: true, - cinemaMode: false, + state = { + isEnded: false, + loading: true, + cinemaMode: false, - stream: null, - spectators: 0, + stream: null, + spectators: 0, - player: null, - decoderInstance: null, - } + player: null, + decoderInstance: null, + } - videoPlayerRef = React.createRef() + videoPlayerRef = React.createRef() - loadDecoder = async (decoder, ...args) => { - if (typeof StreamDecoders[decoder] === "undefined") { - console.error("Protocol not supported") - return false - } + loadDecoder = async (decoder, ...args) => { + if (typeof StreamDecoders[decoder] === "undefined") { + console.error("Protocol not supported") + return false + } - await this.toggleLoading(true) + await this.toggleLoading(true) - // check if decoder is already loaded - if (this.state.decoderInstance) { - if (typeof this.state.decoderInstance.destroy === "function") { - this.state.decoderInstance.destroy() - } + // check if decoder is already loaded + if (this.state.decoderInstance) { + if (typeof this.state.decoderInstance.destroy === "function") { + this.state.decoderInstance.destroy() + } - this.setState({ decoderInstance: null }) - } + this.setState({ decoderInstance: null }) + } - console.log(`Switching decoder to: ${decoder}`) + console.log(`Switching decoder to: ${decoder}`) - const decoderInstance = await StreamDecoders[decoder](...args) + const decoderInstance = await StreamDecoders[decoder](...args) - await this.setState({ - decoderInstance: decoderInstance - }) + await this.setState({ + decoderInstance: decoderInstance, + }) - await this.toggleLoading(false) + await this.toggleLoading(false) - return decoderInstance - } + return decoderInstance + } - loadStream = async (stream_id) => { - let stream = await SpectrumAPI.getLivestream(stream_id).catch((error) => { - console.error(error) - return null - }) + loadStream = async (stream_id) => { + let stream = await SpectrumModel.getLivestream(stream_id).catch( + (error) => { + console.error(error) + return null + }, + ) - if (!stream) { - return false - } + if (!stream) { + return false + } - if (Array.isArray(stream)) { - stream = stream[0] - } + if (Array.isArray(stream)) { + stream = stream[0] + } - console.log("Stream data >", stream) + console.log("Stream data >", stream) - this.setState({ - stream: stream, - spectators: stream.viewers, - }) + this.setState({ + stream: stream, + spectators: stream.viewers, + }) - return stream - } + return stream + } - onSourceEnd = () => { - if (typeof this.state.decoderInstance?.destroy === "function") { - this.state.decoderInstance.destroy() - } + onSourceEnd = () => { + if (typeof this.state.decoderInstance?.destroy === "function") { + this.state.decoderInstance.destroy() + } - this.state.player.destroy() + this.state.player.destroy() - this.setState({ - isEnded: true, - loading: false, - cinemaMode: false, - }) - } + this.setState({ + isEnded: true, + loading: false, + cinemaMode: false, + }) + } - attachPlayer = () => { - // check if user has interacted with the page - const player = new Plyr("#player", { - clickToPlay: false, - autoplay: true, - muted: true, - controls: ["mute", "volume", "fullscreen", "airplay", "options", "settings",], - settings: ["quality"], - }) + attachPlayer = () => { + // check if user has interacted with the page + const player = new Plyr("#player", { + clickToPlay: false, + autoplay: true, + muted: true, + controls: [ + "mute", + "volume", + "fullscreen", + "airplay", + "options", + "settings", + ], + settings: ["quality"], + }) - player.muted = true + player.muted = true - // insert a button to enter to cinema mode - player.elements.buttons.fullscreen.insertAdjacentHTML("beforeBegin", ` + // insert a button to enter to cinema mode + player.elements.buttons.fullscreen.insertAdjacentHTML( + "beforeBegin", + ` - `) + `, + ) - player.elements.buttons.cinema = player.elements.container.querySelector("[data-plyr='cinema']") + player.elements.buttons.cinema = + player.elements.container.querySelector("[data-plyr='cinema']") - player.elements.buttons.cinema.addEventListener("click", () => this.toggleCinemaMode()) + player.elements.buttons.cinema.addEventListener("click", () => + this.toggleCinemaMode(), + ) - this.setState({ - player, - }) - } + this.setState({ + player, + }) + } - componentDidMount = async () => { - this.enterPlayerAnimation() + componentDidMount = async () => { + this.enterPlayerAnimation() - const stream_id = this.props.params.id + const stream_id = this.props.params.id - console.log("Stream ID >", stream_id) + console.log("Stream ID >", stream_id) - this.attachPlayer() + this.attachPlayer() - // get stream info - const stream = await this.loadStream(stream_id) + // get stream info + const stream = await this.loadStream(stream_id) - if (!stream) { - return this.onSourceEnd() - } + if (!stream) { + return this.onSourceEnd() + } - // load the flv decoder (by default) - if (stream) { - if (!stream.sources) { - console.error("Stream sources not found") - return - } + // load the flv decoder (by default) + if (stream) { + if (!stream.sources) { + console.error("Stream sources not found") + return + } - await this.loadDecoder("flv", - this.videoPlayerRef.current, - stream.sources.flv, - { - onSourceEnd: this.onSourceEnd, - } - ) - } - } + await this.loadDecoder( + "flv", + this.videoPlayerRef.current, + stream.sources.flv, + { + onSourceEnd: this.onSourceEnd, + }, + ) + } + } - componentWillUnmount = () => { - if (this.state.player) { - this.state.player.destroy() - } + componentWillUnmount = () => { + if (this.state.player) { + this.state.player.destroy() + } - if (typeof this.state.decoderInstance?.unload === "function") { - this.state.decoderInstance.unload() - } + if (typeof this.state.decoderInstance?.unload === "function") { + this.state.decoderInstance.unload() + } - this.exitPlayerAnimation() + this.exitPlayerAnimation() - this.toggleCinemaMode(false) + this.toggleCinemaMode(false) - if (this.streamInfoInterval) { - clearInterval(this.streamInfoInterval) - } - } + if (this.streamInfoInterval) { + clearInterval(this.streamInfoInterval) + } + } - enterPlayerAnimation = () => { - app.cores.style.applyTemporalVariant("dark") - app.layout.toggleCompactMode(true) - app.layout.toggleCenteredContent(false) - app.controls.toggleUIVisibility(false) - } + enterPlayerAnimation = () => { + app.cores.style.applyTemporalVariant("dark") + app.layout.toggleCompactMode(true) + app.layout.toggleCenteredContent(false) + app.controls.toggleUIVisibility(false) + } - exitPlayerAnimation = () => { - app.cores.style.applyVariant(app.cores.style.currentVariantKey) - app.layout.toggleCompactMode(false) - app.layout.toggleCenteredContent(true) - app.controls.toggleUIVisibility(true) - } + exitPlayerAnimation = () => { + app.cores.style.applyVariant(app.cores.style.currentVariantKey) + app.layout.toggleCompactMode(false) + app.layout.toggleCenteredContent(true) + app.controls.toggleUIVisibility(true) + } - updateQuality = (newQuality) => { - if (this.state.loadedProtocol !== "hls") { - console.error("Unsupported protocol") - return false - } + updateQuality = (newQuality) => { + if (this.state.loadedProtocol !== "hls") { + console.error("Unsupported protocol") + return false + } - this.state.protocolInstance.levels.forEach((level, levelIndex) => { - if (level.height === newQuality) { - console.log("Found quality match with " + newQuality) - this.state.protocolInstance.currentLevel = levelIndex - } - }) - } + this.state.protocolInstance.levels.forEach((level, levelIndex) => { + if (level.height === newQuality) { + console.log("Found quality match with " + newQuality) + this.state.protocolInstance.currentLevel = levelIndex + } + }) + } - toggleLoading = (to) => { - this.setState({ loading: to ?? !this.state.loading }) - } + toggleLoading = (to) => { + this.setState({ loading: to ?? !this.state.loading }) + } - toggleCinemaMode = (to) => { - if (typeof to === "undefined") { - to = !this.state.cinemaMode - } + toggleCinemaMode = (to) => { + if (typeof to === "undefined") { + to = !this.state.cinemaMode + } - this.setState({ cinemaMode: to }) - } + this.setState({ cinemaMode: to }) + } - render() { - return
- {this.props.query.id} -
-
-
app.location.back()} - > - -
+ render() { + return ( +
+ {this.props.query.id} +
+
+
app.location.back()} + > + +
- { - this.state.stream - ? <> -
+ {this.state.stream ? ( + <> +
+ +
+ {!this.state.isEnded && ( +
+ } + > + {this.state.spectators} + +
+ )} +
+
- + {this.state.stream.info && ( +
+
+

+ {this.state.stream.info?.title} +

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

+ { + this.state + .stream.info + ?.description + } +

+ ) + }} +
+
+
+ )} + + ) : ( + + )} +
-
- { - !this.state.isEnded &&
- } - > - {this.state.spectators} - -
- } -
-
+
+ {this.state.isEnded && ( + +

This stream is ended

+
+ )} -
- { - this.state.isEnded && -

- This stream is ended -

-
- } - -
- -
-
- -
-
- { - !this.state.cinemaMode &&
-

Live chat

-
- } - -
-
-
- } -} \ No newline at end of file +
+
+ {!this.state.cinemaMode && ( +
+

+ Live chat +

+
+ )} + +
+
+
+ ) + } +}