parent
6a47b867a4
commit
d703501b78
12 changed files with 4062 additions and 72 deletions
|
|
@ -31,6 +31,8 @@ const state = {
|
|||
onlineUsers: new Set(),
|
||||
};
|
||||
|
||||
|
||||
|
||||
const el = {
|
||||
authScreen: document.getElementById("auth-screen"),
|
||||
loginBtn: document.getElementById("login-btn"),
|
||||
|
|
@ -724,22 +726,35 @@ function ensurePeerConnection(peerId) {
|
|||
|
||||
const pc = new RTCPeerConnection({ iceServers: state.voice.iceServers });
|
||||
|
||||
// Add transceivers in a fixed order to ensure stable SDP m-lines: [Audio, Camera, Screen]
|
||||
// 1. Audio
|
||||
if (state.voice.localStream) {
|
||||
for (const track of state.voice.localStream.getTracks()) {
|
||||
pc.addTrack(track, state.voice.localStream);
|
||||
}
|
||||
pc.addTransceiver(state.voice.localStream.getAudioTracks()[0], {
|
||||
direction: 'sendrecv',
|
||||
streams: [state.voice.localStream]
|
||||
});
|
||||
} else {
|
||||
pc.addTransceiver('audio', { direction: 'sendrecv' });
|
||||
}
|
||||
|
||||
// 2. Camera Video
|
||||
if (state.voice.videoStream) {
|
||||
for (const track of state.voice.videoStream.getTracks()) {
|
||||
pc.addTrack(track, state.voice.videoStream);
|
||||
}
|
||||
pc.addTransceiver(state.voice.videoStream.getVideoTracks()[0], {
|
||||
direction: 'sendrecv',
|
||||
streams: [state.voice.videoStream]
|
||||
});
|
||||
} else {
|
||||
pc.addTransceiver('video', { direction: 'sendrecv' });
|
||||
}
|
||||
|
||||
// 3. Screen Video
|
||||
if (state.voice.screenStream) {
|
||||
for (const track of state.voice.screenStream.getTracks()) {
|
||||
pc.addTrack(track, state.voice.screenStream);
|
||||
}
|
||||
pc.addTransceiver(state.voice.screenStream.getVideoTracks()[0], {
|
||||
direction: 'sendrecv',
|
||||
streams: [state.voice.screenStream]
|
||||
});
|
||||
} else {
|
||||
pc.addTransceiver('video', { direction: 'sendrecv' });
|
||||
}
|
||||
|
||||
pc.onicecandidate = (event) => {
|
||||
|
|
@ -945,11 +960,12 @@ async function toggleVideo() {
|
|||
if (state.voice.ws) {
|
||||
state.voice.ws.send(JSON.stringify({ type: "set_video_status", is_sharing_video: false }));
|
||||
}
|
||||
const trackId = `video-${state.me.id}-camera`;
|
||||
for (const pc of state.voice.peerConnections.values()) {
|
||||
const senders = pc.getSenders();
|
||||
const videoSender = senders.find(s => s.track && s.track.kind === "video" && !s.track.label.toLowerCase().includes('screen'));
|
||||
if (videoSender) pc.removeTrack(videoSender);
|
||||
const transceivers = pc.getTransceivers();
|
||||
const cameraTransceiver = transceivers.find(t => t.receiver.track.kind === 'video' && t.mid === transceivers[1].mid);
|
||||
if (cameraTransceiver) {
|
||||
cameraTransceiver.sender.replaceTrack(null);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
|
|
@ -963,8 +979,9 @@ async function toggleVideo() {
|
|||
}
|
||||
|
||||
for (const pc of state.voice.peerConnections.values()) {
|
||||
for (const track of stream.getTracks()) {
|
||||
pc.addTrack(track, stream);
|
||||
const transceivers = pc.getTransceivers();
|
||||
if (transceivers[1]) {
|
||||
transceivers[1].sender.replaceTrack(stream.getVideoTracks()[0]);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
|
@ -986,9 +1003,10 @@ async function toggleScreenShare() {
|
|||
state.voice.ws.send(JSON.stringify({ type: "set_screen_status", is_sharing_screen: false }));
|
||||
}
|
||||
for (const pc of state.voice.peerConnections.values()) {
|
||||
const senders = pc.getSenders();
|
||||
const screenSender = senders.find(s => s.track && s.track.kind === "video" && (s.track.label.toLowerCase().includes('screen') || s.track.label.toLowerCase().includes('monitor')));
|
||||
if (screenSender) pc.removeTrack(screenSender);
|
||||
const transceivers = pc.getTransceivers();
|
||||
if (transceivers[2]) {
|
||||
transceivers[2].sender.replaceTrack(null);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
|
|
@ -1007,8 +1025,9 @@ async function toggleScreenShare() {
|
|||
}
|
||||
|
||||
for (const pc of state.voice.peerConnections.values()) {
|
||||
for (const track of stream.getTracks()) {
|
||||
pc.addTrack(track, stream);
|
||||
const transceivers = pc.getTransceivers();
|
||||
if (transceivers[2]) {
|
||||
transceivers[2].sender.replaceTrack(stream.getVideoTracks()[0]);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
|
@ -1137,11 +1156,26 @@ function closeMobileMenus() {
|
|||
async function init() {
|
||||
lucide.createIcons();
|
||||
|
||||
try {
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const jwtToken = searchParams.get("token");
|
||||
if (jwtToken) {
|
||||
localStorage.setItem("chattz_token", jwtToken);
|
||||
searchParams.delete("token");
|
||||
const nextQuery = searchParams.toString();
|
||||
const nextUrl = `${location.pathname}${nextQuery ? `?${nextQuery}` : ""}`;
|
||||
history.replaceState(null, "", nextUrl);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to parse token from URL:", e);
|
||||
}
|
||||
|
||||
el.loginBtn.onclick = () => { location.href = "/auth/login"; };
|
||||
|
||||
el.logoutBtn.onclick = async () => {
|
||||
await leaveVoice();
|
||||
await api("/auth/logout", { method: "POST" });
|
||||
localStorage.removeItem("chattz_token");
|
||||
try { await api("/auth/logout", { method: "POST" }); } catch (e) { }
|
||||
location.reload();
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue