Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: restreaming issues #773

Merged
merged 2 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
239 changes: 113 additions & 126 deletions src/lib/components/Channel/Chat/DrawerRestream.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,18 @@

const remove = async () => {
loading = true
await del(`/output?outputId=${selected}`, auth)
await del(`output?outputId=${selected}`, auth)
await getAll()
loading = false
confirm_modal = false
}

const getAll = async () => {
loading = true
urlList = await get('outputs', auth)
loading = false
if ($page.data.user?.userId) {
loading = true
urlList = await get('outputs', auth)
loading = false
}
}

const confirm = (id: string) => {
Expand All @@ -66,16 +68,11 @@
onMount(() => {
getAll()
})

$: cloudFareUrl = payload.url.includes('cloudflare')
$: invalidUrl = !isValidURL(payload.url)

$: disbaled = loading ||
!payload.url ||
!payload.streamKey ||
invalidUrl ||
cloudFareUrl
$: cloudflareUrl = payload.url.includes('cloudflare')
$: invalidUrl = !isValidURL(payload.url)

$: disbaled = loading || !payload.url || !payload.streamKey || invalidUrl || cloudflareUrl
</script>

<div class="drawer drawer-end">
Expand All @@ -98,128 +95,118 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="drawer-side z-50" on:click={overlayClick}>
<label id="overlay" for="restream-drawer" aria-label="close sidebar" class="drawer-overlay" />
<ul class="relative menu p-4 w-96 h-[calc(100%-40px)] overflow-auto bg-base-200 text-base-content m-4 rounded-lg">
<p class="p-3 pt-0 text-xl mb-5 pb-2 border-purple-500 font-semibold border-b-2 flex justify-between items-center">
Restream Urls
<button
class="btn btn-primary btn-sm"
disabled={urlList.length > 9}
on:click={() => {
showAddModal = true
touched = false
}}>
Add url
</button>
</p>

<div class="flex flex-col">
{#each urlList as item}
<div class="bg-base-100 p-4 my-1 rounded flex justify-between items-center">
<div class="flex-1">
<div class="">{item.url}</div>
<div>
<input
value="dummyvalues"
type="password"
placeholder="Type here"
class="bg-transparent"
disabled
/>
<ul
class="relative menu p-4 w-96 h-[calc(100%-40px)] overflow-auto bg-base-200 text-base-content m-4 rounded-lg">
<p
class="p-3 pt-0 text-xl mb-5 pb-2 border-purple-500 font-semibold border-b-2 flex justify-between items-center">
Restream Urls
<button
class="btn btn-primary btn-sm"
disabled={urlList.length > 9}
on:click={() => {
showAddModal = true
touched = false
}}>
Add url
</button>
</p>

<div class="flex flex-col">
{#each urlList as item}
<div class="bg-base-100 p-4 my-1 rounded flex justify-between items-center">
<div class="flex-1">
<div class="">{item.url}</div>
<div>
<input
value="dummyvalues"
type="password"
placeholder="Type here"
class="bg-transparent"
disabled />
</div>
</div>
<button on:click={() => confirm(item._id)} class="btn btn-sm btn-circle btn-ghost">
</button>
</div>
<button on:click={() => confirm(item._id)} class="btn btn-sm btn-circle btn-ghost">
</button>
</div>
{/each}
</div>

<dialog class={`modal ${confirm_modal && 'modal-open'}`}>
<form on:keydown={(event) => event.key != 'Enter'} method="dialog" class="modal-box">
<h3 class="font-bold text-lg">Delete restream url</h3>
<p class="py-4">
{loading ? 'Please wait...' : 'Are you sure you want to delete this Stream Url?'}
</p>
<div class="modal-action">
{#if !loading}
{/each}
</div>

<dialog class={`modal ${confirm_modal && 'modal-open'}`}>
<form on:keydown={(event) => event.key != 'Enter'} method="dialog" class="modal-box">
<h3 class="font-bold text-lg">Delete restream url</h3>
<p class="py-4">
{loading ? 'Please wait...' : 'Are you sure you want to delete this Stream Url?'}
</p>
<div class="modal-action">
{#if !loading}
<button
class="btn"
disabled={loading}
on:click={() => {
confirm_modal = false
}}>Cancel</button>
<button disabled={loading} class="btn btn-primary" on:click={remove}>Yes</button>
{/if}
</div>
</form>
</dialog>

<dialog bind:this={add_output_modal} class={`modal ${showAddModal && 'modal-open'}`}>
<div class="modal-box">
<h3 class="font-bold text-lg">Add new stream</h3>

<div class="form-control w-full pt-4">
<!-- svelte-ignore a11y-label-has-associated-control -->
<label class="label">
<span class="label-text">Server</span>
</label>
<input
bind:value={payload.url}
type="text"
placeholder="Enter server url"
class="input input-bordered w-full max-w-xs input-primary"
on:blur={onBlur} />
{#if touched && invalidUrl}
<div class="text-error text-sm mt-2">Please enter a valid URL</div>
{/if}
{#if touched && !invalidUrl && cloudflareUrl}
<div class="text-error text-sm mt-2">Cloudfare urls not allowed</div>
{/if}
<!-- svelte-ignore a11y-label-has-associated-control -->
<label class="label mt-5">
<span class="label-text">Stream Key</span>
</label>
<input
bind:value={payload.streamKey}
type="text"
placeholder="Enter stream key"
class="input input-bordered w-full max-w-xs input-primary" />
</div>

<div class="modal-action">
<button
class="btn"
disabled={loading}
on:click={() => {
confirm_modal = false
showAddModal = false
payload = {
url: '',
streamKey: ''
}
}}>Cancel</button>
<button disabled={loading} class="btn btn-primary" on:click={remove}>Yes</button>
{/if}
</div>
</form>
</dialog>

<dialog bind:this={add_output_modal} class={`modal ${showAddModal && 'modal-open'}`}>
<div class="modal-box">
<h3 class="font-bold text-lg">Add new stream</h3>


<div class="form-control w-full pt-4">
<!-- svelte-ignore a11y-label-has-associated-control -->
<label class="label">
<span class="label-text">Server</span>
</label>
<input
bind:value={payload.url}
type="text"
placeholder="Enter server url"
class="input input-bordered w-full max-w-xs input-primary"
on:blur={onBlur}
/>
{#if touched && invalidUrl}
<div class="text-error text-sm mt-2">Please enter a valid URL</div>
{/if}
{#if touched && !invalidUrl && cloudFareUrl}
<div class="text-error text-sm mt-2">Cloudfare urls not allowed</div>
{/if}
<!-- svelte-ignore a11y-label-has-associated-control -->
<label class="label mt-5">
<span class="label-text">Stream Key</span>
</label>
<input
bind:value={payload.streamKey}
type="text"
placeholder="Enter stream key"
class="input input-bordered w-full max-w-xs input-primary" />
</div>

<div class="modal-action">
<button
class="btn"
on:click={() => {
showAddModal = false
payload = {
url: '',
streamKey: ''
}
}}>Cancel</button>
<button
disabled={disbaled}
class="btn btn-primary"
on:click={addNew}>
Save
</button>
<button disabled={disbaled} class="btn btn-primary" on:click={addNew}> Save </button>
</div>
</div>
</dialog>

<div class="p-4 absolute left-0 bottom-0 w-full text-center cursor-pointer">
<button
type="button"
class="btn btn-neutral text-white grow w-full"
on:click={() => {
document.getElementById('overlay')?.click()
}}>Cancel</button>
</div>
</dialog>

<div

class="p-4 absolute left-0 bottom-0 w-full text-center cursor-pointer">
<button
type="button"
class="btn btn-neutral text-white grow w-full"
on:click={()=> {
document.getElementById("overlay")?.click()
}}
>Cancel</button>
</div>
</ul>
</div>
</div>

19 changes: 18 additions & 1 deletion src/lib/components/Channel/Stream/StreamControls.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,19 @@
}
}

const sendOutputs = async ({ liveInputUid }: { liveInputUid: string }) => {
if ($is_feature_restream_enabled) {
return await post(
`outputs/send`,
{ liveInputUid },
{
userId: $page.data.user?.userId,
token: $page.data.user?.token
}
)
}
}

const startObsStream = async () => {
const liveInput = await createLiveInput({
channelId: `${$page.params.channelId}`,
Expand All @@ -91,7 +104,7 @@
meta: {
name: `${$page.params.channelId}-${$page.data.user.userId}-obs`
},
recording: { mode: 'off' }
recording: { mode: 'automatic' }
}
})
channel.videoItems = updateVideoItems(channel.videoItems, [liveInput])
Expand All @@ -103,6 +116,7 @@
video: liveInput
}
})
await sendOutputs({ liveInputUid: liveInput.uid })
await sendFcm({
channelId: $page.params.channelId,
channelTitle: channel.title,
Expand Down Expand Up @@ -155,6 +169,7 @@
video: liveInput
}
})
await sendOutputs({ liveInputUid: liveInput.uid })
await sendFcm({
channelId: $page.params.channelId,
channelTitle: channel.title,
Expand Down Expand Up @@ -207,6 +222,7 @@
video: liveInput
}
})
await sendOutputs({ liveInputUid: liveInput.uid })
await sendFcm({
channelId: $page.params.channelId,
channelTitle: channel.title,
Expand Down Expand Up @@ -259,6 +275,7 @@
video: liveInput
}
})
await sendOutputs({ liveInputUid: liveInput.uid })
await sendFcm({
channelId: $page.params.channelId,
channelTitle: channel.title,
Expand Down
23 changes: 10 additions & 13 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,23 +340,20 @@
return new File([u8arr], filename, { type: mime })
}

export const objectMonitor = (object:any) => {
return (currentState:any) => {
export const objectMonitor = (object: any) => {
return (currentState: any) => {
return JSON.stringify(object) !== JSON.stringify(currentState)
}
}

export const isValidURL = (url: string) => {
// const urlPattern = /^https?:\/\/\S+$/i
// return urlPattern.test(url)
const pattern = new RegExp(
'^(https?:\\/\\/)?' + // protocol
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
'(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
'(\\#[-a-z\\d_]*)?$', // fragment locator
'^rtmp:\\/\\/' + // protocol
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name

Check failure

Code scanning / CodeQL

Inefficient regular expression High

This part of the regular expression may cause exponential backtracking on strings starting with '0' and containing many repetitions of '0'.
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
'(\\/[-a-z\\d%_.~+]*)+' + // app name
'$',
'i'
);
return pattern.test(url);
}
)
return pattern.test(url)
}
Loading