mirror of
https://github.com/SrIzan10/helium.git
synced 2026-06-06 00:56:58 +00:00
refactor: move script under template
This commit is contained in:
@@ -1,59 +1,86 @@
|
||||
<template>
|
||||
<div class="flex flex-col items-center justify-center gap-6 mt-10 px-4">
|
||||
<h1>helium</h1>
|
||||
<p>effortless screensharing powered by webrtc</p>
|
||||
<app-code-input />
|
||||
|
||||
<div class="video relative w-full max-w-1/2 aspect-video">
|
||||
<div
|
||||
v-if="!isConnected"
|
||||
class="absolute inset-0 bg-black flex items-center justify-center z-10 text-white"
|
||||
>
|
||||
{{ viewerStore.connectionStatus }}
|
||||
</div>
|
||||
<video
|
||||
ref="videofeedRef"
|
||||
autoplay
|
||||
playsinline
|
||||
controls
|
||||
class="bg-black w-full h-full"
|
||||
@loadeddata="isConnected = true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<NuxtLink to="/stream"><Button>host instead?</Button></NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useWebSocket } from '@vueuse/core';
|
||||
import { useViewerStore } from '~/state/viewer';
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { useWebSocketUrl } from '~/composables/useWebSocketUrl';
|
||||
import { useWebSocket } from "@vueuse/core";
|
||||
import { useViewerStore } from "~/state/viewer";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useWebSocketUrl } from "~/composables/useWebSocketUrl";
|
||||
|
||||
const isConnected = ref(false);
|
||||
const viewerStore = useViewerStore()
|
||||
const { code: codeRef } = storeToRefs(viewerStore)
|
||||
const wsUrl = useWebSocketUrl()
|
||||
const viewerStore = useViewerStore();
|
||||
const { code: codeRef } = storeToRefs(viewerStore);
|
||||
const wsUrl = useWebSocketUrl();
|
||||
const { send } = useWebSocket(wsUrl, {
|
||||
autoReconnect: true,
|
||||
heartbeat: {
|
||||
message: JSON.stringify({ event: 'ping' }),
|
||||
message: JSON.stringify({ event: "ping" }),
|
||||
interval: 15000,
|
||||
},
|
||||
onMessage: async (ws, ev) => {
|
||||
const message = JSON.parse(ev.data)
|
||||
if (message.event === 'offer') {
|
||||
viewerStore.setConnectionStatus('creating rtc peer connections...')
|
||||
const message = JSON.parse(ev.data);
|
||||
if (message.event === "offer") {
|
||||
viewerStore.setConnectionStatus("creating rtc peer connections...");
|
||||
const peerConnection = new RTCPeerConnection({
|
||||
iceServers: [
|
||||
{ urls: 'stun:stun.l.google.com:19302' },
|
||||
{ urls: 'stun:stun1.l.google.com:19302' },
|
||||
{ urls: "stun:stun.l.google.com:19302" },
|
||||
{ urls: "stun:stun1.l.google.com:19302" },
|
||||
{
|
||||
urls: 'turn:5.161.207.54:3478',
|
||||
username: 'username',
|
||||
credential: 'password',
|
||||
urls: "turn:5.161.207.54:3478",
|
||||
username: "username",
|
||||
credential: "password",
|
||||
},
|
||||
{
|
||||
urls: 'turn:5.161.49.183:3478',
|
||||
username: 'username',
|
||||
credential: 'password',
|
||||
urls: "turn:5.161.49.183:3478",
|
||||
username: "username",
|
||||
credential: "password",
|
||||
},
|
||||
{
|
||||
urls: 'turn:135.181.147.65:3478',
|
||||
username: 'username',
|
||||
credential: 'password',
|
||||
urls: "turn:135.181.147.65:3478",
|
||||
username: "username",
|
||||
credential: "password",
|
||||
},
|
||||
{
|
||||
urls: 'turn:5.78.83.26:3478',
|
||||
username: 'username',
|
||||
credential: 'password',
|
||||
urls: "turn:5.78.83.26:3478",
|
||||
username: "username",
|
||||
credential: "password",
|
||||
},
|
||||
{
|
||||
urls: 'turn:5.223.48.157:3478',
|
||||
username: 'username',
|
||||
credential: 'password',
|
||||
urls: "turn:5.223.48.157:3478",
|
||||
username: "username",
|
||||
credential: "password",
|
||||
},
|
||||
],
|
||||
iceTransportPolicy: 'relay',
|
||||
iceTransportPolicy: "relay",
|
||||
});
|
||||
viewerStore.setPeerConnection(peerConnection);
|
||||
|
||||
|
||||
peerConnection.ontrack = (event) => {
|
||||
viewerStore.setConnectionStatus('got some tracks!')
|
||||
viewerStore.setConnectionStatus("got some tracks!");
|
||||
if (event.streams && event.streams[0] && videofeedRef.value) {
|
||||
videofeedRef.value.srcObject = event.streams[0];
|
||||
}
|
||||
@@ -61,92 +88,91 @@ const { send } = useWebSocket(wsUrl, {
|
||||
|
||||
peerConnection.onicecandidate = (event) => {
|
||||
if (event.candidate) {
|
||||
viewerStore.setConnectionStatus(`got an ice candidate (type: ${event.candidate.type})`)
|
||||
send(JSON.stringify({
|
||||
event: 'ice-candidate',
|
||||
targetId: message.senderId,
|
||||
candidate: event.candidate,
|
||||
}))
|
||||
viewerStore.setConnectionStatus(
|
||||
`got an ice candidate (type: ${event.candidate.type})`,
|
||||
);
|
||||
send(
|
||||
JSON.stringify({
|
||||
event: "ice-candidate",
|
||||
targetId: message.senderId,
|
||||
candidate: event.candidate,
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
peerConnection.onconnectionstatechange = () => {
|
||||
viewerStore.setConnectionStatus(`connection state: ${peerConnection.connectionState}`);
|
||||
|
||||
if (peerConnection.connectionState === 'connected') {
|
||||
viewerStore.setConnectionStatus('connected!');
|
||||
viewerStore.setConnectionStatus(
|
||||
`connection state: ${peerConnection.connectionState}`,
|
||||
);
|
||||
|
||||
if (peerConnection.connectionState === "connected") {
|
||||
viewerStore.setConnectionStatus("connected!");
|
||||
}
|
||||
};
|
||||
|
||||
peerConnection.oniceconnectionstatechange = () => {
|
||||
viewerStore.setConnectionStatus(`ice connection state: ${peerConnection.iceConnectionState}`);
|
||||
viewerStore.setConnectionStatus(
|
||||
`ice connection state: ${peerConnection.iceConnectionState}`,
|
||||
);
|
||||
};
|
||||
|
||||
peerConnection.onicegatheringstatechange = () => {
|
||||
viewerStore.setConnectionStatus(`ice gathering state: ${peerConnection.iceGatheringState}`);
|
||||
viewerStore.setConnectionStatus(
|
||||
`ice gathering state: ${peerConnection.iceGatheringState}`,
|
||||
);
|
||||
};
|
||||
|
||||
viewerStore.setConnectionStatus('sending an sdp description')
|
||||
await peerConnection.setRemoteDescription(new RTCSessionDescription(message.sdp));
|
||||
|
||||
viewerStore.setConnectionStatus('sending an answer')
|
||||
viewerStore.setConnectionStatus("sending an sdp description");
|
||||
await peerConnection.setRemoteDescription(
|
||||
new RTCSessionDescription(message.sdp),
|
||||
);
|
||||
|
||||
viewerStore.setConnectionStatus("sending an answer");
|
||||
const answer = await peerConnection.createAnswer();
|
||||
await peerConnection.setLocalDescription(answer);
|
||||
|
||||
send(JSON.stringify({
|
||||
event: 'answer',
|
||||
targetId: message.senderId,
|
||||
sdp: answer,
|
||||
}))
|
||||
|
||||
send(
|
||||
JSON.stringify({
|
||||
event: "answer",
|
||||
targetId: message.senderId,
|
||||
sdp: answer,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (message.event === 'ice-candidate') {
|
||||
if (viewerStore.peerConnection && viewerStore.peerConnection.remoteDescription) {
|
||||
viewerStore.setConnectionStatus(`got an ice candidate from remote peer (type: ${message.candidate.type})`)
|
||||
await viewerStore.peerConnection.addIceCandidate(new RTCIceCandidate(message.candidate));
|
||||
|
||||
if (message.event === "ice-candidate") {
|
||||
if (
|
||||
viewerStore.peerConnection &&
|
||||
viewerStore.peerConnection.remoteDescription
|
||||
) {
|
||||
viewerStore.setConnectionStatus(
|
||||
`got an ice candidate from remote peer (type: ${message.candidate.type})`,
|
||||
);
|
||||
await viewerStore.peerConnection.addIceCandidate(
|
||||
new RTCIceCandidate(message.candidate),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const videofeedRef = ref<HTMLVideoElement|null>(null);
|
||||
const videofeedRef = ref<HTMLVideoElement | null>(null);
|
||||
|
||||
const startWebRTCConnection = async () => {
|
||||
send(JSON.stringify({
|
||||
event: 'join-room',
|
||||
roomId: viewerStore.code,
|
||||
}))
|
||||
}
|
||||
send(
|
||||
JSON.stringify({
|
||||
event: "join-room",
|
||||
roomId: viewerStore.code,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
watch(codeRef, (newCode) => {
|
||||
// sort of a safeguard bc only 6 digit codes end up getting passed
|
||||
if (newCode.length === 6) {
|
||||
startWebRTCConnection();
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col items-center justify-center gap-6 mt-10 px-4">
|
||||
<h1>helium</h1>
|
||||
<p>effortless screensharing powered by webrtc</p>
|
||||
<app-code-input />
|
||||
|
||||
<div class="video relative w-full max-w-1/2 aspect-video">
|
||||
<div v-if="!isConnected" class="absolute inset-0 bg-black flex items-center justify-center z-10 text-white">
|
||||
{{ viewerStore.connectionStatus }}
|
||||
</div>
|
||||
<video
|
||||
ref="videofeedRef"
|
||||
autoplay
|
||||
playsinline
|
||||
controls
|
||||
class="bg-black w-full h-full"
|
||||
@loadeddata="isConnected = true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<NuxtLink to="/stream"><Button>host instead?</Button></NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
<template>
|
||||
<div class="h-96 w-full border rounded-md overflow-hidden">
|
||||
<ClientOnly
|
||||
><MonacoEditor :options="editorOptions" class="h-full w-full" lang="json"
|
||||
/></ClientOnly>
|
||||
<ClientOnly>
|
||||
<MonacoEditor
|
||||
:options="editorOptions"
|
||||
class="h-full w-full"
|
||||
lang="json"
|
||||
/>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,97 +1,112 @@
|
||||
<script setup lang="ts">
|
||||
import { useWebSocket } from '@vueuse/core';
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { useStreamerStore } from '~/state/streamer';
|
||||
import { useWebSocketUrl } from '~/composables/useWebSocketUrl';
|
||||
import PresetSelect from '~/components/app/PresetSelect.vue';
|
||||
<template>
|
||||
<div class="flex flex-col items-center justify-center gap-6 mt-10 px-4">
|
||||
<div class="flex space-x-4 items-center">
|
||||
<Button @click="startScreenShare"> screenshare </Button>
|
||||
<PresetSelect />
|
||||
</div>
|
||||
<p v-if="streamerStore.code" class="font-mono">{{ streamerStore.code }}</p>
|
||||
<video ref="videofeedRef" autoplay playsinline muted></video>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
const streamerStore = useStreamerStore()
|
||||
<script setup lang="ts">
|
||||
import { useWebSocket } from "@vueuse/core";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useStreamerStore } from "~/state/streamer";
|
||||
import { useWebSocketUrl } from "~/composables/useWebSocketUrl";
|
||||
import PresetSelect from "~/components/app/PresetSelect.vue";
|
||||
|
||||
const streamerStore = useStreamerStore();
|
||||
const videofeedRef = ref<HTMLVideoElement | null>(null);
|
||||
const localStream = ref<MediaStream | null>(null);
|
||||
const wsUrl = useWebSocketUrl()
|
||||
const wsUrl = useWebSocketUrl();
|
||||
|
||||
const { send } = useWebSocket(wsUrl, {
|
||||
autoReconnect: true,
|
||||
heartbeat: {
|
||||
message: JSON.stringify({ event: 'ping' }),
|
||||
message: JSON.stringify({ event: "ping" }),
|
||||
interval: 15000,
|
||||
},
|
||||
onMessage: async (ws, ev) => {
|
||||
const message = JSON.parse(ev.data)
|
||||
const message = JSON.parse(ev.data);
|
||||
|
||||
if (message.event === 'room-created') {
|
||||
streamerStore.setCode(message.roomId)
|
||||
if (message.event === "room-created") {
|
||||
streamerStore.setCode(message.roomId);
|
||||
}
|
||||
|
||||
if (message.event === 'viewer-joined') {
|
||||
if (message.event === "viewer-joined") {
|
||||
const peerConnection = new RTCPeerConnection({
|
||||
iceServers: [
|
||||
{ urls: 'stun:stun.l.google.com:19302' },
|
||||
{ urls: 'stun:stun1.l.google.com:19302' },
|
||||
{ urls: "stun:stun.l.google.com:19302" },
|
||||
{ urls: "stun:stun1.l.google.com:19302" },
|
||||
{
|
||||
urls: 'turn:5.161.207.54:3478',
|
||||
username: 'username',
|
||||
credential: 'password',
|
||||
urls: "turn:5.161.207.54:3478",
|
||||
username: "username",
|
||||
credential: "password",
|
||||
},
|
||||
{
|
||||
urls: 'turn:5.161.49.183:3478',
|
||||
username: 'username',
|
||||
credential: 'password',
|
||||
urls: "turn:5.161.49.183:3478",
|
||||
username: "username",
|
||||
credential: "password",
|
||||
},
|
||||
{
|
||||
urls: 'turn:135.181.147.65:3478',
|
||||
username: 'username',
|
||||
credential: 'password',
|
||||
urls: "turn:135.181.147.65:3478",
|
||||
username: "username",
|
||||
credential: "password",
|
||||
},
|
||||
{
|
||||
urls: 'turn:5.78.83.26:3478',
|
||||
username: 'username',
|
||||
credential: 'password',
|
||||
urls: "turn:5.78.83.26:3478",
|
||||
username: "username",
|
||||
credential: "password",
|
||||
},
|
||||
{
|
||||
urls: 'turn:5.223.48.157:3478',
|
||||
username: 'username',
|
||||
credential: 'password',
|
||||
urls: "turn:5.223.48.157:3478",
|
||||
username: "username",
|
||||
credential: "password",
|
||||
},
|
||||
],
|
||||
iceTransportPolicy: 'relay',
|
||||
iceTransportPolicy: "relay",
|
||||
});
|
||||
streamerStore.addPeerConnection(message.viewerId, peerConnection)
|
||||
streamerStore.addPeerConnection(message.viewerId, peerConnection);
|
||||
|
||||
if (localStream.value) {
|
||||
localStream.value.getTracks().forEach(track => {
|
||||
localStream.value.getTracks().forEach((track) => {
|
||||
peerConnection.addTrack(track, localStream.value!);
|
||||
});
|
||||
}
|
||||
|
||||
peerConnection.onicecandidate = (event) => {
|
||||
if (event.candidate) {
|
||||
send(JSON.stringify({
|
||||
event: 'ice-candidate',
|
||||
targetId: message.viewerId,
|
||||
candidate: event.candidate,
|
||||
}))
|
||||
send(
|
||||
JSON.stringify({
|
||||
event: "ice-candidate",
|
||||
targetId: message.viewerId,
|
||||
candidate: event.candidate,
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const offer = await peerConnection.createOffer();
|
||||
await peerConnection.setLocalDescription(offer);
|
||||
|
||||
send(JSON.stringify({
|
||||
event: 'offer',
|
||||
targetId: message.viewerId,
|
||||
sdp: offer,
|
||||
}))
|
||||
send(
|
||||
JSON.stringify({
|
||||
event: "offer",
|
||||
targetId: message.viewerId,
|
||||
sdp: offer,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (message.event === 'ice-candidate') {
|
||||
if (message.event === "ice-candidate") {
|
||||
const pc = streamerStore.peerConnections[message.from];
|
||||
if (pc) {
|
||||
await pc.addIceCandidate(new RTCIceCandidate(message.candidate));
|
||||
}
|
||||
}
|
||||
|
||||
if (message.event === 'answer') {
|
||||
if (message.event === "answer") {
|
||||
const pc = streamerStore.peerConnections[message.from];
|
||||
if (pc) {
|
||||
await pc.setRemoteDescription(new RTCSessionDescription(message.sdp));
|
||||
@@ -112,21 +127,10 @@ async function startScreenShare() {
|
||||
videofeedRef.value.srcObject = stream;
|
||||
}
|
||||
|
||||
send(JSON.stringify({
|
||||
event: 'create-room',
|
||||
}))
|
||||
send(
|
||||
JSON.stringify({
|
||||
event: "create-room",
|
||||
}),
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col items-center justify-center gap-6 mt-10 px-4">
|
||||
<div class="flex space-x-4 items-center">
|
||||
<Button @click="startScreenShare">
|
||||
screenshare
|
||||
</Button>
|
||||
<PresetSelect />
|
||||
</div>
|
||||
<p v-if="streamerStore.code" class="font-mono">{{ streamerStore.code }}</p>
|
||||
<video ref="videofeedRef" autoplay playsinline muted></video>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user