endPair/disconnect: WebRTC tamamen kapatılıyor + tiles temizleniyor

sdk/Peer.js — endPair():
  - this.rtc?.destroy() eklendi
  - Yerel akışlar durur, RTCPeerConnection kapanır

sdk/index.js — sinyal handler'ları:
  - end/pair: peer.rtc?.destroy() (alan taraf da kapatır)
  - peer/disconnect: peer.rtc?.destroy() (WebSocket kopunca RTC da kapanır)

public/studio/Studio.js:
  _clearPeerTiles(peerId):
    - data-peer-id dataset'i ile local + remote grid'den tile'ları kaldırır
    - Gizli <audio> elemanını srcObject=null + remove() ile temizler
    - CSS.escape ile güvenli selector
  tile.dataset.peerId: _addLocalTile ve _addRemoteTile her ikisinde eklendi
  tile._audioEl: ses tile'larında referans saklanır (clearPeerTiles için)
  _watchIncoming: peer.rtc.on('disconnected') → clearPeerTiles + rebuildPeerItems
  end/pair handler: clearPeerTiles(from) çağrısı eklendi
  peer/disconnect handler: clearPeerTiles(peer.socketId) çağrısı eklendi
  _addLocalTile/_addRemoteTile: peerLabel yerine peer objesi alır

go test -race ./... — yeşil

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
abdussamedulutas 2026-06-17 14:59:47 +03:00
parent d468c95adf
commit 5ebd111af0
3 changed files with 45 additions and 12 deletions

View File

@ -62,14 +62,16 @@ export default class Studio {
this._setStatus('online', `${this._peerLabel(peer)} eşleşmesi kuruldu`);
});
// Eşleşme bitti (karşı taraf koptu veya endPair çağrıldı)
// Eşleşme bitti — tiles + RTC (SDK zaten destroy çağırdı, ama tiles Studio'ya ait)
this.mwse.me.on('end/pair', (from) => {
if (typeof from === 'string') this._clearPeerTiles(from);
this._rebuildPeerItems();
this._setStatus('', `${typeof from === 'string' ? from.slice(-8) : ''} ayrıldı`);
});
// WebSocket bağlantısı kopunca (sayfa yenileme, ağ kesilmesi)
// WebSocket koptu → RTC da kapandı (SDK destroy çağırdı)
this.mwse.me.on('peer/disconnect', peer => {
this._clearPeerTiles(peer.socketId);
this._rebuildPeerItems();
this._setStatus('error', `${this._peerLabel(peer)} bağlantısı kesildi`);
});
@ -173,24 +175,29 @@ export default class Studio {
}
// Yerel (gönderilen) akış tile'ı ekle
_addLocalTile(label, stream, peerLabel) {
const hasVideo = stream.getVideoTracks().length > 0;
_addLocalTile(label, stream, peer) {
const peerLabel = typeof peer === 'string' ? peer : this._peerLabel(peer);
const peerId = peer?.socketId ?? peerLabel;
const hasVideo = stream.getVideoTracks().length > 0;
const tile = this._makeTile(
label,
`${peerLabel}`,
stream,
hasVideo,
true, // muted (geri besleme yok)
() => tile.remove() && this._updatePanelVisibility()
() => { tile.remove(); this._updatePanelVisibility(); }
);
tile.dataset.peerId = peerId;
this._localGrid.appendChild(tile);
this._updatePanelVisibility();
}
// Uzak (gelen) track tile'ı → ses kontrollü
_addRemoteTile(track, streams, peerLabel) {
const isVideo = track.kind === 'video';
const stream = streams?.[0] ?? new MediaStream([track]);
_addRemoteTile(track, streams, peer) {
const peerLabel = typeof peer === 'string' ? peer : this._peerLabel(peer);
const peerId = peer?.socketId ?? peerLabel;
const isVideo = track.kind === 'video';
const stream = streams?.[0] ?? new MediaStream([track]);
const tile = document.createElement('div');
tile.className = `mwse-stream-tile${isVideo ? '' : ' mwse-stream-tile--audio'}`;
@ -212,6 +219,8 @@ export default class Studio {
mediaEl.muted = false;
mediaEl.srcObject = stream;
document.body.appendChild(mediaEl);
// tile'a referans ekle ki _clearPeerTiles kapatabilsin
tile._audioEl = mediaEl;
const icon = document.createElement('div');
icon.className = 'mwse-stream-tile__audio-icon';
@ -258,6 +267,7 @@ export default class Studio {
info.append(lbl, peerEl, muteBtn, closeBtn);
tile.appendChild(info);
tile.dataset.peerId = peerId;
this._remoteGrid.appendChild(tile);
this._updatePanelVisibility();
@ -588,7 +598,7 @@ export default class Studio {
const label = dev.label || dev.deviceId.slice(-6);
if (peer.rtc._streams?.has(label)) peer.rtc.removeStream(label);
peer.rtc.addStream(label, stream);
this._addLocalTile(label, stream, this._peerLabel(peer));
this._addLocalTile(label, stream, peer);
this._view.popTo(4); this._pushStreamsColumn(peer);
} else {
this._previewStream(stream, dev.label || title);
@ -662,7 +672,7 @@ export default class Studio {
if (peer.rtc._streams?.has(type)) peer.rtc.removeStream(type);
peer.rtc.addStream(type, stream);
this._addLocalTile(type, stream, this._peerLabel(peer));
this._addLocalTile(type, stream, peer);
this._setStatus('online', `${type}${this._peerLabel(peer)}`);
this._view.popTo(3);
this._pushStreamsColumn(peer);
@ -690,6 +700,21 @@ export default class Studio {
peer.rtc.connect({ polite: this.mwse.me.socketId < peer.socketId });
}
// Bir peer'a ait tüm tile'ları temizle (pair bitti veya disconnect)
_clearPeerTiles(peerId) {
for (const grid of [this._localGrid, this._remoteGrid]) {
if (!grid) continue;
// dataset.peerId → attribute adı: data-peer-id
for (const tile of [...grid.querySelectorAll(`[data-peer-id="${CSS.escape(peerId)}"]`)]) {
// Gizli <audio> elemanlarını da kapat
const audio = tile._audioEl;
if (audio) { audio.srcObject = null; audio.remove(); }
tile.remove();
}
}
this._updatePanelVisibility();
}
// Gelen track'leri izle → panel'e ekle
_watchIncoming(peer) {
// RTC başlatılmazsa gelen :rtcpack: sinyalleri receive() içinde _neg=null
@ -697,9 +722,15 @@ export default class Studio {
this._ensureRTC(peer);
peer.rtc.on('track', (track, streams) => {
this._addRemoteTile(track, streams, this._peerLabel(peer));
this._addRemoteTile(track, streams, peer);
this._setStatus('online', `${this._peerLabel(peer)} ${track.kind}`);
});
// RTC kapandığında (endPair, peer/disconnect) tile'ları temizle
peer.rtc.on('disconnected', () => {
this._clearPeerTiles(peer.socketId);
this._rebuildPeerItems();
});
}
_previewStream(stream, label) {

View File

@ -125,6 +125,7 @@ export default class Peer extends MWSEEventTarget {
async endPair() {
await this.mwse.EventPooling.request({ type: 'end/pair', to: this.socketId });
this.mwse.pairs.delete(this.socketId);
this.rtc?.destroy(); // WebRTC bağlantısını kapat, akışları durdur
this.forget();
}

View File

@ -236,8 +236,8 @@ export default class MWSE {
ep.signal('peer/disconnect', ({ id }) => {
const peer = this.peer(id, true);
this.pairs.delete(id);
peer.rtc?.destroy(); // WebSocket kopunca (sayfa yenileme vb.) RTC da kapat
peer.emit('disconnect');
// me üzerinden de yay, Studio dinleyebilsin.
this.peer('me').emit('peer/disconnect', peer);
});
@ -253,6 +253,7 @@ export default class MWSE {
ep.signal('end/pair', ({ from, info }) => {
const peer = this.peer(from, true);
this.pairs.delete(from);
peer.rtc?.destroy(); // karşı taraf endPair çağırdığında da RTC kapat
peer.emit('end/pair', info);
this.peer('me').emit('end/pair', from, info);
});