mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 10:34:17 +00:00
update to streaming service version 5_b7
This commit is contained in:
parent
3ce53e9dbd
commit
d398173010
@ -15,10 +15,10 @@ import "./index.less"
|
||||
|
||||
export default (props) => {
|
||||
const [L_Profiles, R_Profiles, E_Profiles, M_Profiles] = app.cores.api.useRequest(Livestream.getProfiles)
|
||||
|
||||
const [profileData, setProfileData] = React.useState(null)
|
||||
const [selectedProfileId, setSelectedProfileId] = React.useState(null)
|
||||
|
||||
const [isConnected, setIsConnected] = React.useState(false)
|
||||
const [loadingChanges, setLoadingChanges] = React.useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (R_Profiles) {
|
||||
@ -30,23 +30,14 @@ export default (props) => {
|
||||
}
|
||||
}, [R_Profiles])
|
||||
|
||||
if (E_Profiles) {
|
||||
console.error(E_Profiles)
|
||||
React.useEffect(() => {
|
||||
if (selectedProfileId) {
|
||||
console.log(R_Profiles)
|
||||
setProfileData(R_Profiles.find((profile) => profile._id === selectedProfileId))
|
||||
}
|
||||
}, [selectedProfileId])
|
||||
|
||||
return <antd.Result
|
||||
status="error"
|
||||
title="Failed to load profiles"
|
||||
subTitle="Failed to load profiles, please try again later"
|
||||
/>
|
||||
}
|
||||
|
||||
if (L_Profiles) {
|
||||
return <antd.Skeleton active />
|
||||
}
|
||||
|
||||
const profileData = R_Profiles.find((profile) => profile._id === selectedProfileId)
|
||||
|
||||
const handleCreateProfile = async (profile_name) => {
|
||||
async function handleCreateProfile(profile_name) {
|
||||
if (!profile_name) {
|
||||
return false
|
||||
}
|
||||
@ -70,7 +61,7 @@ export default (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleCurrentProfileDataUpdate = async (newProfileData) => {
|
||||
async function handleCurrentProfileDataUpdate(newProfileData) {
|
||||
if (!profileData) {
|
||||
return
|
||||
}
|
||||
@ -95,7 +86,7 @@ export default (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleCurrentProfileDelete = async () => {
|
||||
async function handleCurrentProfileDelete() {
|
||||
if (!profileData) {
|
||||
return
|
||||
}
|
||||
@ -126,7 +117,7 @@ export default (props) => {
|
||||
})
|
||||
}
|
||||
|
||||
const onClickEditInfo = () => {
|
||||
async function onClickEditInfo() {
|
||||
if (!profileData) {
|
||||
return
|
||||
}
|
||||
@ -140,7 +131,7 @@ export default (props) => {
|
||||
})
|
||||
}
|
||||
|
||||
const regenerateStreamingKey = async () => {
|
||||
async function regenerateStreamingKey() {
|
||||
if (!profileData) {
|
||||
return
|
||||
}
|
||||
@ -163,7 +154,58 @@ export default (props) => {
|
||||
})
|
||||
}
|
||||
|
||||
return <div className="streamingControlPanel">
|
||||
async function updateOption(key, value) {
|
||||
if (!profileData) {
|
||||
return
|
||||
}
|
||||
|
||||
setLoadingChanges(`option:${key}`)
|
||||
|
||||
const result = await Livestream.postProfile({
|
||||
profile_id: profileData._id,
|
||||
profile_name: profileData.profile_name,
|
||||
options: {
|
||||
[key]: value
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error(err)
|
||||
app.message.error(`Failed to update option`)
|
||||
setLoadingChanges(false)
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
if (result) {
|
||||
console.log("Updated options >", result)
|
||||
|
||||
setProfileData((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
...result,
|
||||
}
|
||||
})
|
||||
setLoadingChanges(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (E_Profiles) {
|
||||
console.error(E_Profiles)
|
||||
|
||||
return <antd.Result
|
||||
status="error"
|
||||
title="Failed to load profiles"
|
||||
subTitle="Failed to load profiles, please try again later"
|
||||
/>
|
||||
}
|
||||
|
||||
if (L_Profiles) {
|
||||
return <antd.Skeleton active />
|
||||
}
|
||||
|
||||
return <div
|
||||
className="streamingControlPanel"
|
||||
disabled={!profileData || loadingChanges}
|
||||
>
|
||||
<div className="streamingControlPanel_header_thumbnail">
|
||||
<img
|
||||
src={
|
||||
@ -272,23 +314,49 @@ export default (props) => {
|
||||
<h2><Icons.Tool />Additional options</h2>
|
||||
|
||||
<div className="content">
|
||||
<span>Enable DVR</span>
|
||||
<p className="label">
|
||||
<Icons.MdFiberDvr /> DVR
|
||||
</p>
|
||||
|
||||
<span className="description">
|
||||
This function will save a copy of your stream with its entire duration.
|
||||
You can get this copy after finishing this livestream
|
||||
</span>
|
||||
|
||||
<div className="value">
|
||||
<antd.Switch
|
||||
checked={profileData?.options?.dvr ?? false}
|
||||
onChange={false}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="content">
|
||||
<span>Private mode</span>
|
||||
<p className="label">
|
||||
<Icons.MdPrivateConnectivity /> Private mode
|
||||
</p>
|
||||
|
||||
<span className="description">
|
||||
When this is enabled, only users with the livestream url can access the stream. Your stream will not be visible on the app.
|
||||
</span>
|
||||
|
||||
<span
|
||||
className="description"
|
||||
style={{
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
You must restart the livestream to apply the changes.
|
||||
</span>
|
||||
|
||||
<div className="value">
|
||||
<antd.Switch
|
||||
checked={profileData?.options?.private ?? false}
|
||||
onChange={false}
|
||||
onChange={(value) => {
|
||||
updateOption("private", value)
|
||||
}}
|
||||
loading={loadingChanges === "option:private"}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -298,17 +366,12 @@ export default (props) => {
|
||||
<h2><Icons.Link /> URL Information</h2>
|
||||
|
||||
<div className="content">
|
||||
<span>AAC URL (Only Audio)</span>
|
||||
|
||||
<div className="inline_field">
|
||||
<span>
|
||||
{profileData?.addresses?.aac ?? "No AAC URL available"}
|
||||
</span>
|
||||
<div className="title">
|
||||
<p>HLS URL</p>
|
||||
<p>[6s~12s latency]</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="content">
|
||||
<span>HLS URL</span>
|
||||
<span className="description">This protocol is highly compatible with a multitude of devices and services. Recommended for general use.</span>
|
||||
|
||||
<div className="inline_field">
|
||||
<span>
|
||||
@ -318,7 +381,12 @@ export default (props) => {
|
||||
</div>
|
||||
|
||||
<div className="content">
|
||||
<span>FLV URL</span>
|
||||
<div className="title">
|
||||
<p>FLV URL</p>
|
||||
<p>[2s~5s latency]</p>
|
||||
</div>
|
||||
|
||||
<span className="description">This protocol operates at better latency and quality than HLS, but is less compatible for most devices.</span>
|
||||
|
||||
<div className="inline_field">
|
||||
<span>
|
||||
@ -326,6 +394,21 @@ export default (props) => {
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="content">
|
||||
<div className="title">
|
||||
<p>MP3 URL (Only Audio)</p>
|
||||
<p>[2s ~ 5s latency]</p>
|
||||
</div>
|
||||
|
||||
<span className="description">This protocol will only return an audio file. The maximum quality compatible with this codec will be used (320Kbps, 48KHz)</span>
|
||||
|
||||
<div className="inline_field">
|
||||
<span>
|
||||
{profileData?.addresses?.mp3 ?? "No MP3 URL available"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="streaming_configs_panel">
|
||||
|
@ -11,6 +11,10 @@
|
||||
|
||||
gap: 20px;
|
||||
|
||||
&[disabled]{
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.inline_field {
|
||||
background-color: var(--background-color-accent);
|
||||
|
||||
@ -133,13 +137,48 @@
|
||||
|
||||
width: 100%;
|
||||
|
||||
gap: 7px;
|
||||
|
||||
.title {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
svg {
|
||||
font-size: 1rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
|
||||
align-items: center;
|
||||
|
||||
gap: 8px;
|
||||
|
||||
svg {
|
||||
font-size: 1.3rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 0.8rem;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
import { StreamingProfile } from "@shared-classes/DbModels"
|
||||
|
||||
export default {
|
||||
method: "GET",
|
||||
route: "/profile/visibility",
|
||||
middlewares: ["withAuthentication"],
|
||||
fn: async (req, res) => {
|
||||
let { ids } = req.query
|
||||
|
||||
if (typeof ids === "string") {
|
||||
ids = [ids]
|
||||
}
|
||||
|
||||
let visibilities = await StreamingProfile.find({
|
||||
_id: { $in: ids }
|
||||
})
|
||||
|
||||
visibilities = visibilities.map((visibility) => {
|
||||
return [visibility._id.toString(), visibility.options.private]
|
||||
})
|
||||
|
||||
return res.json(visibilities)
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ export default {
|
||||
|
||||
profile._id = profile._id.toString()
|
||||
|
||||
profile.stream_key = `${req.user.username}:${profile._id}?secret=${profile.stream_key}`
|
||||
profile.stream_key = `${req.user.username}__${profile._id}?secret=${profile.stream_key}`
|
||||
|
||||
return profile
|
||||
})
|
||||
|
@ -5,7 +5,7 @@ export default {
|
||||
route: "/streams",
|
||||
fn: async (req, res) => {
|
||||
if (req.query.username) {
|
||||
const stream = await fetchRemoteStreams(`${req.query.username}${req.query.profile_id ? `:${req.query.profile_id}` : ""}`)
|
||||
const stream = await fetchRemoteStreams(`${req.query.username}${req.query.profile_id ? `__${req.query.profile_id}` : ""}`)
|
||||
|
||||
if (!stream) {
|
||||
return res.status(404).json({
|
||||
|
@ -30,7 +30,7 @@ export default {
|
||||
})
|
||||
}
|
||||
|
||||
const [username, profile_id] = app.split("/")[1].split(":")
|
||||
const [username, profile_id] = app.split("/")[1].split("__")
|
||||
|
||||
if (user.username !== username) {
|
||||
return res.status(403).json({
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { StreamingProfile } from "@shared-classes/DbModels"
|
||||
import NewStreamingProfile from "@services/newStreamingProfile"
|
||||
|
||||
const AllowedChangesFields = ["profile_name", "info", "options"]
|
||||
|
||||
export default {
|
||||
method: "POST",
|
||||
route: "/streaming/profile",
|
||||
@ -34,9 +36,11 @@ export default {
|
||||
|
||||
if (currentProfile && profile_id) {
|
||||
// update the profile
|
||||
currentProfile.profile_name = profile_name
|
||||
currentProfile.info = info
|
||||
currentProfile.options = options
|
||||
AllowedChangesFields.forEach((field) => {
|
||||
if (req.body[field]) {
|
||||
currentProfile[field] = req.body[field]
|
||||
}
|
||||
})
|
||||
|
||||
await currentProfile.save()
|
||||
} else {
|
||||
|
@ -17,7 +17,6 @@ export default async (stream_id) => {
|
||||
url: apiURI,
|
||||
params: {
|
||||
stream: stream_id,
|
||||
useFetch: true,
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error(err)
|
||||
@ -34,10 +33,8 @@ export default async (stream_id) => {
|
||||
streamings = data
|
||||
}
|
||||
|
||||
streamings = streamings.map(async (stream) => {
|
||||
const { video, audio, clients, name } = stream
|
||||
|
||||
const profile_id = name.split(":")[1]
|
||||
streamings = streamings.map(async (entry) => {
|
||||
const { stream, profile_id } = entry
|
||||
|
||||
let profile = await StreamingProfile.findById(profile_id)
|
||||
|
||||
@ -62,13 +59,10 @@ export default async (stream_id) => {
|
||||
return {
|
||||
profile_id: profile._id,
|
||||
info: profile.info,
|
||||
name: name,
|
||||
name: stream,
|
||||
streamUrl: `${user.username}?profile=${profile._id}`,
|
||||
user,
|
||||
video,
|
||||
audio,
|
||||
connectedClients: clients ?? 0,
|
||||
sources: lodash.pick(sources, ["rtmp", "hls", "flv", "aac"]),
|
||||
user,
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -3,13 +3,13 @@ const streamingServerAPIAddress = process.env.STREAMING_API_SERVER ?? ""
|
||||
const streamingServerAPIUri = `${streamingServerAPIAddress.startsWith("https") ? "https" : "http"}://${streamingServerAPIAddress.split("://")[1]}`
|
||||
|
||||
export default (username, profile_id) => {
|
||||
const streamId = `${username}${profile_id ? `:${profile_id}` : ""}`
|
||||
const streamId = `${username}${profile_id ? `__${profile_id}` : ""}`
|
||||
|
||||
return {
|
||||
ingest: process.env.STREAMING_INGEST_SERVER,
|
||||
rtmp: `${streamingServerAPIUri}/${streamId}`,
|
||||
hls: `${streamingServerAPIUri}/stream/${streamId}/src.m3u8`,
|
||||
flv: `${streamingServerAPIUri}/stream/${streamId}/src.flv`,
|
||||
aac: `${streamingServerAPIUri}/stream/${streamId}/src.aac`,
|
||||
hls: `${streamingServerAPIUri}/stream/hls/${streamId}`,
|
||||
flv: `${streamingServerAPIUri}/stream/flv/${streamId}`,
|
||||
mp3: `${streamingServerAPIUri}/stream/mp3/${streamId}`,
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ export default {
|
||||
options: {
|
||||
type: Object,
|
||||
default: {
|
||||
connection_protected: true,
|
||||
private: false,
|
||||
chatEnabled: true,
|
||||
drvEnabled: false,
|
||||
|
Loading…
x
Reference in New Issue
Block a user