mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 10:34:17 +00:00
support for spectrum 6
This commit is contained in:
parent
6e38d41293
commit
eeb84add9e
@ -15,334 +15,346 @@ import { FiLink } from "react-icons/fi"
|
|||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
const ProfileData = (props) => {
|
const ProfileData = (props) => {
|
||||||
if (!props.profile_id) {
|
if (!props.profile_id) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const [loading, setLoading] = React.useState(false)
|
const [loading, setLoading] = React.useState(false)
|
||||||
const [fetching, setFetching] = React.useState(true)
|
const [fetching, setFetching] = React.useState(true)
|
||||||
const [error, setError] = React.useState(null)
|
const [error, setError] = React.useState(null)
|
||||||
const [profile, setProfile] = React.useState(null)
|
const [profile, setProfile] = React.useState(null)
|
||||||
|
|
||||||
async function fetchData(profile_id) {
|
async function fetchData(profile_id) {
|
||||||
setFetching(true)
|
setFetching(true)
|
||||||
|
|
||||||
const result = await Streaming.getProfile({ profile_id }).catch((error) => {
|
const result = await Streaming.getProfile({ profile_id }).catch(
|
||||||
console.error(error)
|
(error) => {
|
||||||
setError(error)
|
console.error(error)
|
||||||
return null
|
setError(error)
|
||||||
})
|
return null
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
setProfile(result)
|
setProfile(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
setFetching(false)
|
setFetching(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleChange(key, value) {
|
async function handleChange(key, value) {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
|
||||||
const result = await Streaming.createOrUpdateStream({
|
const result = await Streaming.createOrUpdateStream({
|
||||||
[key]: value,
|
[key]: value,
|
||||||
_id: profile._id,
|
_id: profile._id,
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
antd.message.error("Failed to update")
|
antd.message.error("Failed to update")
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
antd.message.success("Updated")
|
antd.message.success("Updated")
|
||||||
setProfile(result)
|
setProfile(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleDelete() {
|
async function handleDelete() {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
|
||||||
const result = await Streaming.deleteProfile({ profile_id: profile._id }).catch((error) => {
|
const result = await Streaming.deleteProfile({
|
||||||
console.error(error)
|
profile_id: profile._id,
|
||||||
antd.message.error("Failed to delete")
|
}).catch((error) => {
|
||||||
return false
|
console.error(error)
|
||||||
})
|
antd.message.error("Failed to delete")
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
antd.message.success("Deleted")
|
antd.message.success("Deleted")
|
||||||
app.eventBus.emit("app:profile_deleted", profile._id)
|
app.eventBus.emit("app:profile_deleted", profile._id)
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleEditName() {
|
async function handleEditName() {
|
||||||
app.layout.modal.open("name_editor", ProfileCreator, {
|
app.layout.modal.open("name_editor", ProfileCreator, {
|
||||||
props: {
|
props: {
|
||||||
editValue: profile.profile_name,
|
editValue: profile.profile_name,
|
||||||
onEdit: async (value) => {
|
onEdit: async (value) => {
|
||||||
await handleChange("profile_name", value)
|
await handleChange("profile_name", value)
|
||||||
app.eventBus.emit("app:profiles_updated", profile._id)
|
app.eventBus.emit("app:profiles_updated", profile._id)
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData(props.profile_id)
|
fetchData(props.profile_id)
|
||||||
}, [props.profile_id])
|
}, [props.profile_id])
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return <antd.Result
|
return (
|
||||||
status="warning"
|
<antd.Result
|
||||||
title="Error"
|
status="warning"
|
||||||
subTitle={error.message}
|
title="Error"
|
||||||
extra={[
|
subTitle={error.message}
|
||||||
<antd.Button
|
extra={[
|
||||||
type="primary"
|
<antd.Button
|
||||||
onClick={() => fetchData(props.profile_id)}
|
type="primary"
|
||||||
>
|
onClick={() => fetchData(props.profile_id)}
|
||||||
Retry
|
>
|
||||||
</antd.Button>
|
Retry
|
||||||
]}
|
</antd.Button>,
|
||||||
/>
|
]}
|
||||||
}
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (fetching) {
|
if (fetching) {
|
||||||
return <antd.Skeleton
|
return <antd.Skeleton active />
|
||||||
active
|
}
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div className="tvstudio-profile-data">
|
return (
|
||||||
<div
|
<div className="tvstudio-profile-data">
|
||||||
className="tvstudio-profile-data-header"
|
<div className="tvstudio-profile-data-header">
|
||||||
>
|
<img
|
||||||
<img
|
className="tvstudio-profile-data-header-image"
|
||||||
className="tvstudio-profile-data-header-image"
|
src={profile.info?.thumbnail}
|
||||||
src={profile.info?.thumbnail}
|
/>
|
||||||
/>
|
<div className="tvstudio-profile-data-header-content">
|
||||||
<div className="tvstudio-profile-data-header-content">
|
<EditableText
|
||||||
<EditableText
|
value={profile.info?.title ?? "Untitled"}
|
||||||
value={profile.info?.title ?? "Untitled"}
|
className="tvstudio-profile-data-header-title"
|
||||||
className="tvstudio-profile-data-header-title"
|
style={{
|
||||||
style={{
|
"--fontSize": "2rem",
|
||||||
"--fontSize": "2rem",
|
"--fontWeight": "800",
|
||||||
"--fontWeight": "800"
|
}}
|
||||||
}}
|
onSave={(newValue) => {
|
||||||
onSave={(newValue) => {
|
return handleChange("title", newValue)
|
||||||
return handleChange("title", newValue)
|
}}
|
||||||
}}
|
disabled={loading}
|
||||||
disabled={loading}
|
/>
|
||||||
/>
|
<EditableText
|
||||||
<EditableText
|
value={profile.info?.description ?? "No description"}
|
||||||
value={profile.info?.description ?? "No description"}
|
className="tvstudio-profile-data-header-description"
|
||||||
className="tvstudio-profile-data-header-description"
|
style={{
|
||||||
style={{
|
"--fontSize": "1rem",
|
||||||
"--fontSize": "1rem",
|
}}
|
||||||
}}
|
onSave={(newValue) => {
|
||||||
onSave={(newValue) => {
|
return handleChange("description", newValue)
|
||||||
return handleChange("description", newValue)
|
}}
|
||||||
}}
|
disabled={loading}
|
||||||
disabled={loading}
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="tvstudio-profile-data-field">
|
<div className="tvstudio-profile-data-field">
|
||||||
<div className="tvstudio-profile-data-field-header">
|
<div className="tvstudio-profile-data-field-header">
|
||||||
<MdOutlineWifiTethering />
|
<MdOutlineWifiTethering />
|
||||||
<span>Server</span>
|
<span>Server</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field">
|
<div className="key-value-field">
|
||||||
<div className="key-value-field-key">
|
<div className="key-value-field-key">
|
||||||
<span>Ingestion URL</span>
|
<span>Ingestion URL</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field-value">
|
<div className="key-value-field-value">
|
||||||
<span>
|
<span>{profile.ingestion_url}</span>
|
||||||
{profile.ingestion_url}
|
</div>
|
||||||
</span>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="key-value-field">
|
<div className="key-value-field">
|
||||||
<div className="key-value-field-key">
|
<div className="key-value-field-key">
|
||||||
<span>Stream Key</span>
|
<span>Stream Key</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field-value">
|
<div className="key-value-field-value">
|
||||||
<HiddenText
|
<HiddenText value={profile.stream_key} />
|
||||||
value={profile.stream_key}
|
</div>
|
||||||
/>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="tvstudio-profile-data-field">
|
<div className="tvstudio-profile-data-field">
|
||||||
<div className="tvstudio-profile-data-field-header">
|
<div className="tvstudio-profile-data-field-header">
|
||||||
<GrConfigure />
|
<GrConfigure />
|
||||||
<span>Configuration</span>
|
<span>Configuration</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field">
|
<div className="key-value-field">
|
||||||
<div className="key-value-field-key">
|
<div className="key-value-field-key">
|
||||||
<IoMdEyeOff />
|
<IoMdEyeOff />
|
||||||
<span> Private Mode</span>
|
<span> Private Mode</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field-description">
|
<div className="key-value-field-description">
|
||||||
<p>When this is enabled, only users with the livestream url can access the stream.</p>
|
<p>
|
||||||
</div>
|
When this is enabled, only users with the livestream
|
||||||
|
url can access the stream.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field-content">
|
<div className="key-value-field-content">
|
||||||
<antd.Switch
|
<antd.Switch
|
||||||
checked={profile.options.private}
|
checked={profile.options.private}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
onChange={(value) => handleChange("private", value)}
|
onChange={(value) => handleChange("private", value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field-description">
|
<div className="key-value-field-description">
|
||||||
<p style={{ fontWeight: "bold" }}>Must restart the livestream to apply changes</p>
|
<p style={{ fontWeight: "bold" }}>
|
||||||
</div>
|
Must restart the livestream to apply changes
|
||||||
</div>
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field">
|
<div className="key-value-field">
|
||||||
<div className="key-value-field-key">
|
<div className="key-value-field-key">
|
||||||
<GrStorage />
|
<GrStorage />
|
||||||
<span> DVR [beta]</span>
|
<span> DVR [beta]</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field-description">
|
<div className="key-value-field-description">
|
||||||
<p>Save a copy of your stream with its entire duration. You can download this copy after finishing this livestream.</p>
|
<p>
|
||||||
</div>
|
Save a copy of your stream with its entire duration.
|
||||||
|
You can download this copy after finishing this
|
||||||
|
livestream.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field-content">
|
<div className="key-value-field-content">
|
||||||
<antd.Switch
|
<antd.Switch disabled loading={loading} />
|
||||||
disabled
|
</div>
|
||||||
loading={loading}
|
</div>
|
||||||
/>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
{profile.sources && (
|
||||||
profile.sources && <div className="tvstudio-profile-data-field">
|
<div className="tvstudio-profile-data-field">
|
||||||
<div className="tvstudio-profile-data-field-header">
|
<div className="tvstudio-profile-data-field-header">
|
||||||
<FiLink />
|
<FiLink />
|
||||||
<span>Media URL</span>
|
<span>Media URL</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field">
|
<div className="key-value-field">
|
||||||
<div className="key-value-field-key">
|
<div className="key-value-field-key">
|
||||||
<span>HLS</span>
|
<span>HLS</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field-description">
|
<div className="key-value-field-description">
|
||||||
<p>This protocol is highly compatible with a multitude of devices and services. Recommended for general use.</p>
|
<p>
|
||||||
</div>
|
This protocol is highly compatible with a
|
||||||
|
multitude of devices and services. Recommended
|
||||||
|
for general use.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field-value">
|
<div className="key-value-field-value">
|
||||||
<span>
|
<span>{profile.sources.hls}</span>
|
||||||
{profile.sources.hls}
|
</div>
|
||||||
</span>
|
</div>
|
||||||
</div>
|
<div className="key-value-field">
|
||||||
</div>
|
<div className="key-value-field-key">
|
||||||
<div className="key-value-field">
|
<span>RTSP [tcp]</span>
|
||||||
<div className="key-value-field-key">
|
</div>
|
||||||
<span>FLV</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="key-value-field-description">
|
<div className="key-value-field-description">
|
||||||
<p>This protocol operates at better latency and quality than HLS, but is less compatible for most devices.</p>
|
<p>
|
||||||
</div>
|
This protocol has the lowest possible latency
|
||||||
|
and the best quality. A compatible player is
|
||||||
|
required.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field-value">
|
<div className="key-value-field-value">
|
||||||
<span>
|
<span>{profile.sources.rtsp}</span>
|
||||||
{profile.sources.flv}
|
</div>
|
||||||
</span>
|
</div>
|
||||||
</div>
|
<div className="key-value-field">
|
||||||
</div>
|
<div className="key-value-field-key">
|
||||||
<div className="key-value-field">
|
<span>RTSPT [vrchat]</span>
|
||||||
<div className="key-value-field-key">
|
</div>
|
||||||
<span>RTSP [tcp]</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="key-value-field-description">
|
<div className="key-value-field-description">
|
||||||
<p>This protocol has the lowest possible latency and the best quality. A compatible player is required.</p>
|
<p>
|
||||||
</div>
|
This protocol has the lowest possible latency
|
||||||
|
and the best quality available. Only works for
|
||||||
|
VRChat video players.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field-value">
|
<div className="key-value-field-value">
|
||||||
<span>
|
<span>
|
||||||
{profile.sources.rtsp}
|
{profile.sources.rtsp.replace(
|
||||||
</span>
|
"rtsp://",
|
||||||
</div>
|
"rtspt://",
|
||||||
</div>
|
)}
|
||||||
<div className="key-value-field">
|
</span>
|
||||||
<div className="key-value-field-key">
|
</div>
|
||||||
<span>HTML Viewer</span>
|
</div>
|
||||||
</div>
|
<div className="key-value-field">
|
||||||
|
<div className="key-value-field-key">
|
||||||
|
<span>HTML Viewer</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field-description">
|
<div className="key-value-field-description">
|
||||||
<p>Share a link to easily view your stream on any device with a web browser.</p>
|
<p>
|
||||||
</div>
|
Share a link to easily view your stream on any
|
||||||
|
device with a web browser.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field-value">
|
<div className="key-value-field-value">
|
||||||
<span>
|
<span>{profile.sources.html}</span>
|
||||||
{profile.sources.html}
|
</div>
|
||||||
</span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div className="tvstudio-profile-data-field">
|
<div className="tvstudio-profile-data-field">
|
||||||
<div className="tvstudio-profile-data-field-header">
|
<div className="tvstudio-profile-data-field-header">
|
||||||
<span>Other</span>
|
<span>Other</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field">
|
<div className="key-value-field">
|
||||||
<div className="key-value-field-key">
|
<div className="key-value-field-key">
|
||||||
<span>Delete profile</span>
|
<span>Delete profile</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field-content">
|
<div className="key-value-field-content">
|
||||||
<antd.Popconfirm
|
<antd.Popconfirm
|
||||||
title="Delete the profile"
|
title="Delete the profile"
|
||||||
description="Once deleted, the profile cannot be recovered."
|
description="Once deleted, the profile cannot be recovered."
|
||||||
onConfirm={handleDelete}
|
onConfirm={handleDelete}
|
||||||
okText="Yes"
|
okText="Yes"
|
||||||
cancelText="No"
|
cancelText="No"
|
||||||
>
|
>
|
||||||
<antd.Button
|
<antd.Button danger loading={loading}>
|
||||||
danger
|
Delete
|
||||||
loading={loading}
|
</antd.Button>
|
||||||
>
|
</antd.Popconfirm>
|
||||||
Delete
|
</div>
|
||||||
</antd.Button>
|
</div>
|
||||||
</antd.Popconfirm>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="key-value-field">
|
<div className="key-value-field">
|
||||||
<div className="key-value-field-key">
|
<div className="key-value-field-key">
|
||||||
<span>Change profile name</span>
|
<span>Change profile name</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="key-value-field-content">
|
<div className="key-value-field-content">
|
||||||
<antd.Button
|
<antd.Button loading={loading} onClick={handleEditName}>
|
||||||
loading={loading}
|
Change
|
||||||
onClick={handleEditName}
|
</antd.Button>
|
||||||
>
|
</div>
|
||||||
Change
|
</div>
|
||||||
</antd.Button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ProfileData
|
export default ProfileData
|
@ -50,26 +50,56 @@ const StreamDecoders = {
|
|||||||
|
|
||||||
return decoderInstance
|
return decoderInstance
|
||||||
},
|
},
|
||||||
hls: (player, source) => {
|
hls: (player, source, options = {}) => {
|
||||||
|
if (!player) {
|
||||||
|
console.error("Player is not defined")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if (!source) {
|
if (!source) {
|
||||||
console.error("Stream source is not defined")
|
console.error("Stream source is not defined")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const hlsInstance = new Hls({
|
const hlsInstance = new Hls({
|
||||||
autoStartLoad: true,
|
maxLiveSyncPlaybackRate: 1.5,
|
||||||
|
strategy: "bandwidth",
|
||||||
|
autoplay: true,
|
||||||
|
xhrSetup: (xhr) => {
|
||||||
|
if (options.authToken) {
|
||||||
|
xhr.setRequestHeader(
|
||||||
|
"Authorization",
|
||||||
|
`Bearer ${options.authToken}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
hlsInstance.attachMedia(player.current)
|
if (options.authToken) {
|
||||||
|
source += `?token=${options.authToken}`
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Loading media hls >", source, options)
|
||||||
|
|
||||||
|
hlsInstance.attachMedia(player)
|
||||||
|
|
||||||
|
// when media attached, load source
|
||||||
hlsInstance.on(Hls.Events.MEDIA_ATTACHED, () => {
|
hlsInstance.on(Hls.Events.MEDIA_ATTACHED, () => {
|
||||||
hlsInstance.loadSource(source)
|
hlsInstance.loadSource(source)
|
||||||
|
|
||||||
hlsInstance.on(Hls.Events.MANIFEST_PARSED, (event, data) => {
|
|
||||||
console.log(`${data.levels.length} quality levels found`)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// process quality and tracks levels
|
||||||
|
hlsInstance.on(Hls.Events.MANIFEST_PARSED, (event, data) => {
|
||||||
|
console.log(`${data.levels.length} quality levels found`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// resume to the last position when player resume playback
|
||||||
|
player.addEventListener("play", () => {
|
||||||
|
console.log("Syncing to last position")
|
||||||
|
player.currentTime = hlsInstance.liveSyncPosition
|
||||||
|
})
|
||||||
|
|
||||||
|
// handle errors
|
||||||
hlsInstance.on(Hls.Events.ERROR, (event, data) => {
|
hlsInstance.on(Hls.Events.ERROR, (event, data) => {
|
||||||
console.error(event, data)
|
console.error(event, data)
|
||||||
|
|
||||||
@ -129,6 +159,8 @@ export default class StreamViewer extends React.Component {
|
|||||||
|
|
||||||
const decoderInstance = await StreamDecoders[decoder](...args)
|
const decoderInstance = await StreamDecoders[decoder](...args)
|
||||||
|
|
||||||
|
console.log(decoderInstance)
|
||||||
|
|
||||||
await this.setState({
|
await this.setState({
|
||||||
decoderInstance: decoderInstance,
|
decoderInstance: decoderInstance,
|
||||||
})
|
})
|
||||||
@ -176,7 +208,7 @@ export default class StreamViewer extends React.Component {
|
|||||||
|
|
||||||
attachPlayer = () => {
|
attachPlayer = () => {
|
||||||
// check if user has interacted with the page
|
// check if user has interacted with the page
|
||||||
const player = new Plyr("#player", {
|
const player = new Plyr(this.videoPlayerRef.current, {
|
||||||
clickToPlay: false,
|
clickToPlay: false,
|
||||||
autoplay: true,
|
autoplay: true,
|
||||||
muted: true,
|
muted: true,
|
||||||
@ -219,6 +251,8 @@ export default class StreamViewer extends React.Component {
|
|||||||
this.enterPlayerAnimation()
|
this.enterPlayerAnimation()
|
||||||
this.attachPlayer()
|
this.attachPlayer()
|
||||||
|
|
||||||
|
console.log("custom token> ", this.props.query["token"])
|
||||||
|
|
||||||
// load stream
|
// load stream
|
||||||
const stream = await this.loadStream(this.props.params.id)
|
const stream = await this.loadStream(this.props.params.id)
|
||||||
|
|
||||||
@ -234,11 +268,12 @@ export default class StreamViewer extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.loadDecoder(
|
await this.loadDecoder(
|
||||||
"flv",
|
"hls",
|
||||||
this.videoPlayerRef.current,
|
this.videoPlayerRef.current,
|
||||||
stream.sources.flv,
|
stream.sources.hls,
|
||||||
{
|
{
|
||||||
onSourceEnd: this.onSourceEnd,
|
onSourceEnd: this.onSourceEnd,
|
||||||
|
authToken: this.props.query["token"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user