diff --git a/Source/HTTPServer.js b/Source/HTTPServer.js
index b549be8..ab99942 100644
--- a/Source/HTTPServer.js
+++ b/Source/HTTPServer.js
@@ -30,12 +30,11 @@ app.get("/test",(request, response)=>{
app.get("/index.js.map",(request, response)=>{
response.sendFile(resolve("./script/index.js.map"))
});
-app.get("/webrtc.js",(request, response)=>{
- response.sendFile(resolve("./script/webrtc.js"))
-});
-app.get("/webrtc.adapter.js",(request, response)=>{
- response.sendFile(resolve("./script/webrtc.adapter.js"))
+app.get("/stream",(request, response)=>{
+ response.sendFile(resolve("./public/index.html"))
});
+app.use("/stream",express.static(resolve("./public")));
+
app.get("/",(request, response)=>{
response.sendFile(resolve("./script/index.html"))
});
diff --git a/public/index.css b/public/index.css
new file mode 100644
index 0000000..11f8050
--- /dev/null
+++ b/public/index.css
@@ -0,0 +1,93 @@
+html,body{
+ margin: 0;
+ height: 100%;
+}
+body{
+ background-color: #141414;
+}
+.root{
+ gap: 10px;
+ padding: 10px;
+ height: 100%;
+ display: flex;
+ flex-wrap: nowrap;
+ flex-direction: row;
+ box-sizing: border-box;
+}
+.dialoque{
+ flex: 1;
+ display: flex;
+ flex-wrap: nowrap;
+ flex-direction: row;
+ position: relative;
+}
+.videolist{
+ flex: 0 0 200px;
+ display: flex;
+ flex-wrap: nowrap;
+ flex-direction: column;
+ gap: 10px;
+ overflow: auto;
+ padding-right: 10px;
+ cursor: pointer;
+}
+.dialoque #primaryVideo{
+ width: 100%;
+ height: 100%;
+ box-sizing: border-box;
+ object-fit: contain;
+ background-color: transparent/*#282828*/;
+ border-radius: 10px;
+ z-index: 2;
+}
+.dialoque #primaryVideoShadow{
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ border-radius: 10px;
+ position: absolute;
+ z-index: 1;
+ left: 0;
+ top: 0;
+}
+.dialoque #secondaryVideo{
+ width: 300px;
+ height: 200px;
+ min-width: 300px;
+ max-width: 30%;
+ min-height: 200px;
+ max-height: 35%;
+ position: absolute;
+ right: 10px;
+ bottom: 10px;
+ background-color: #383838;
+ border-radius: 10px;
+ z-index: 3;
+ outline-offset: 0px;
+ outline-style: solid;
+ outline-width: 5px;
+ outline-color: rgba(0,0,0,.5);
+}
+.videolist .frame{
+ max-width: 200px;
+ object-fit: cover;
+ border-radius: 10px;
+ border: solid 3px transparent;
+}
+.videolist .frame.active{
+ border: solid 3px green;
+ box-shadow: 0px 0px 20px -10px green;
+}
+video{
+ image-rendering: pixelated;
+}
+::-webkit-scrollbar {
+ width: 6px;
+}
+::-webkit-scrollbar-thumb {
+ background: white;
+ border-radius: 10px;
+}
+::-webkit-scrollbar-track {
+ background-color: #ffffff1f;
+}
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..8945683
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+ saQüt Video Streaming
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/index.js b/public/index.js
new file mode 100644
index 0000000..c7193a4
--- /dev/null
+++ b/public/index.js
@@ -0,0 +1,259 @@
+/**
+ * @type {import("./MWSE/index").default}
+ */
+let mwse;
+/**
+ * @type {string}
+ */
+let mySocketId;
+/**
+ * @type {string}
+ */
+let myIPAddress;
+/**
+ * @type {string}
+ */
+let myNumber;
+/**
+ * @type {string}
+ */
+let roomid;
+/**
+ * @type {import("./MWSE/Room").default}
+ */
+let room;
+/**
+ * @type {MediaStream}
+ */
+let primaryVideoContent;
+/**
+ * @type {HTMLVideoElement}
+ */
+let primaryVideo;
+/**
+ * @type {HTMLVideoElement}
+ */
+let primaryVideoShadow;
+/**
+ * @type {HTMLVideoElement}
+ */
+let secondaryVideo;
+/**
+ * @type {MediaStream}
+ */
+let outgoingStream;
+/**
+ * @type {MediaStream}
+ */
+let outgoingStreamOnlyVideo;
+/**
+ * @type {HTMLDivElement}
+ */
+let videoContainer = document.querySelector(".videolist");
+
+function connect()
+{
+ mwse = new MWSE({
+ endpoint: "wss://ws.saqut.com"
+ });
+
+ mwse.scope(beginEngine);
+}
+/**
+ * @type {HTMLVideoElement}
+ */
+let activeVideo;
+
+function setPrimaryVideo(video, soundOn)
+{
+ primaryVideo.srcObject = video;
+ primaryVideoShadow.srcObject = video;
+ if(soundOn == undefined)
+ {
+ primaryVideo.muted = 1;
+ primaryVideo.volume = 0;
+ }else if(soundOn){
+ primaryVideo.muted = 0;
+ primaryVideo.volume = 1;
+ }else{
+ primaryVideo.muted = 0;
+ primaryVideo.volume = 0;
+ }
+}
+function setSecondaryVideo(video)
+{
+ secondaryVideo.srcObject = video;
+}
+function templateVideo(name, stream)
+{
+ let i = document.createElement("video");
+ i.muted = 1;
+ i.classList.add("frame")
+ i.playsInline = 1;
+ i.autoplay = 1;
+ i.dataset.name = name;
+ if(stream) i.srcObject = stream;
+ return i;
+}
+function addVideoList(name, stream, peer)
+{
+ if(!videoContainer.querySelector(`[name="${name}"]`))
+ {
+ let video = templateVideo(name, stream);
+ video.dataset.user = peer.socketId;
+ video.onclick = function(){
+ if(activeVideo)
+ {
+ activeVideo.classList.remove("active");
+ };
+ video.classList.add("active");
+ activeVideo = video;
+ setPrimaryVideo(stream, true);
+ };
+ videoContainer.appendChild(video);
+ }
+}
+
+function removeVideoList(name)
+{
+ if(videoContainer.querySelector(`[data-name="${name}"]`))
+ {
+ let k = videoContainer.querySelector(`[data-name="${name}"]`);
+ let user = k.dataset.user;
+ if(k.dataset.user == activeVideo.dataset.user)
+ {
+ setPrimaryVideo(outgoingStreamOnlyVideo, false);
+ }
+ k.remove();
+ }
+}
+
+async function beginEngine()
+{
+ let me = mwse.peer("me");
+ me.disablePairAuth();
+ mySocketId = me.socketId;
+ myIPAddress = await mwse.virtualPressure.allocAPIPAddress();
+ myNumber = await mwse.virtualPressure.allocAPNumber();
+
+ let url = new URL(window.location);
+ roomid = url.searchParams.get("room");
+ if(!!roomid == 0)
+ {
+ let hash = window.crypto.randomUUID();
+ url.searchParams.set("room", hash);
+ window.location = url.href;
+ };
+
+ connectRoom(roomid);
+};
+
+window.addEventListener("load", () => {
+ primaryVideo = document.querySelector("#primaryVideo");
+ secondaryVideo = document.querySelector("#secondaryVideo");
+ primaryVideoShadow = document.querySelector("#primaryVideoShadow");
+ connect()
+});
+
+async function startOutgoingWebcam()
+{
+ outgoingStream = await navigator.mediaDevices.getUserMedia({video: true, audio: true});
+ outgoingStreamOnlyVideo = new MediaStream(outgoingStream);
+ outgoingStreamOnlyVideo.removeTrack(outgoingStreamOnlyVideo.getAudioTracks()[0])
+}
+
+async function connectRoom()
+{
+ await startOutgoingWebcam();
+
+ room = mwse.room({
+ name: roomid,
+ joinType: "free",
+ accessType: "private",
+ description: "Private free joined room",
+ ifexistsJoin: true,
+ notifyActionEjected: true,
+ notifyActionInvite: false,
+ notifyActionJoined: true
+ });
+ await room.createRoom();
+
+ room.on("join", peer => IncomingPeer(peer, true));
+ room.on("eject", peer => OutgoingPeer(peer));
+
+ for (const peer of await room.fetchPeers()) {
+ IncomingPeer(peer)
+ }
+ if(!primaryVideoContent)
+ {
+ setPrimaryVideo(outgoingStreamOnlyVideo);
+ }
+ setSecondaryVideo(outgoingStreamOnlyVideo);
+
+ addVideoList("My Webcam",outgoingStreamOnlyVideo, mwse.peer("me"))
+
+}
+/**
+ * @param {import("./MWSE/Peer").default} peer
+ */
+function IncomingPeer(peer,activeConnect)
+{
+ let sendedOTP = false;
+ peer.createRTC({
+ iceServers:[{
+ urls: "turn:161.97.136.175:3478",
+ username: "argist-eu-east-25",
+ credential: "ee7df17eed35f4cf5a207777f3c0cd7d3b1901a5de7aff52ea55742289d7fee2"
+ },{
+ urls: "stun:stun.l.google.com:19302"
+ },{
+ urls: "stun:stun1.l.google.com:19302"
+ },{
+ urls: "stun:stun2.l.google.com:19302"
+ },{
+ urls: "stun:stun3.l.google.com:19302"
+ },{
+ urls: "stun:stun4.l.google.com:19302"
+ }],
+ bundlePolicy: "max-compat",
+ iceCandidatePoolSize: 0,
+ iceTransportPolicy: "all"
+ });
+ if(activeConnect)
+ {
+ peer.rtc.connect();
+ }
+ peer.rtc.on('connected',() => {
+ if(!activeConnect && !sendedOTP)
+ {
+ sendedOTP = true;
+ peer.rtc.sendStream(outgoingStream, "Webcam", {});
+ }
+ });
+ peer.rtc.on('disconnected',() => {
+ removeVideoList(peer.streamY, peer);
+ });
+ peer.rtc.on("stream:added", ({stream,name}) => {
+ peer.streamY = peer.socketId + " | " + name + " - " + stream.id;
+ addVideoList(peer.socketId + " | " + name + " - " + stream.id,stream, peer);
+
+ if(!primaryVideoContent)
+ {
+ primaryVideoContent = stream;
+ setPrimaryVideo(primaryVideoContent, true);
+ }
+
+ if(activeConnect && !sendedOTP)
+ {
+ sendedOTP = true;
+ peer.rtc.sendStream(outgoingStream, "Webcam", {});
+ }
+ })
+}
+/**
+ * @param {import("./MWSE/Peer").default} peer
+ */
+function OutgoingPeer(peer)
+{
+ removeVideoList(peer.streamY, peer);
+}
\ No newline at end of file