From a21fb76ac019b2fdd885824957e0956a8aa0d88b Mon Sep 17 00:00:00 2001
From: voluntas <nakai@shiguredo.jp>
Date: Thu, 29 Feb 2024 13:51:53 +0900
Subject: [PATCH] =?UTF-8?q?sendrecv=20=E3=82=92=20class=20=E5=8C=96?=
 =?UTF-8?q?=E3=81=99=E3=82=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 examples/sendrecv/index.html |  10 +--
 examples/sendrecv/main.mjs   | 108 --------------------------
 examples/sendrecv/main.mts   | 142 +++++++++++++++++++++++++++++++++++
 tests/sendrecv.spec.ts       |   8 +-
 4 files changed, 151 insertions(+), 117 deletions(-)
 delete mode 100644 examples/sendrecv/main.mjs
 create mode 100644 examples/sendrecv/main.mts

diff --git a/examples/sendrecv/index.html b/examples/sendrecv/index.html
index 17411a81..e6ea1050 100644
--- a/examples/sendrecv/index.html
+++ b/examples/sendrecv/index.html
@@ -11,8 +11,8 @@ <h1>Sendrecv test</h1>
     <div style="display: flex;">
       <div>
         <h2>sendrecv1</h2>
-        <button id="start-sendrecv1">start</button>
-        <button id="stop-sendrecv1">stop</button><br />
+        <button id="sendrecv1-start">start</button>
+        <button id="sendrecv1-stop">stop</button><br />
         <video id="sendrecv1-local-video" autoplay="" playsinline="" controls=""
           style="width: 320px; height: 240px; border: 1px solid black;"></video>
         <div id="sendrecv1-connection-id"></div>
@@ -20,8 +20,8 @@ <h2>sendrecv1</h2>
       </div>
       <div>
         <h2>sendrecv2</h2>
-        <button id="start-sendrecv2">start</button>
-        <button id="stop-sendrecv2">stop</button><br />
+        <button id="sendrecv2-start">start</button>
+        <button id="sendrecv2-stop">stop</button><br />
         <video id="sendrecv2-local-video" autoplay="" playsinline="" controls=""
           style="width: 320px; height: 240px; border: 1px solid black;"></video>
         <div id="sendrecv2-connection-id"></div>
@@ -30,7 +30,7 @@ <h2>sendrecv2</h2>
     </div>
   </div>
 
-  <script type="module" src="./main.mjs"></script>
+  <script type="module" src="./main.mts"></script>
 </body>
 
 </html>
\ No newline at end of file
diff --git a/examples/sendrecv/main.mjs b/examples/sendrecv/main.mjs
deleted file mode 100644
index 8f51e842..00000000
--- a/examples/sendrecv/main.mjs
+++ /dev/null
@@ -1,108 +0,0 @@
-import Sora from '../../dist/sora.mjs'
-
-const SORA_SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL
-const SORA_CHANNEL_ID_PREFIX = import.meta.env.VITE_SORA_CHANNEL_ID_PREFIX || ''
-const SORA_CHANNEL_ID_SUFFIX = import.meta.env.VITE_SORA_CHANNEL_ID_SUFFIX || ''
-const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN || ''
-
-const channelId = `${SORA_CHANNEL_ID_PREFIX}sendrecv${SORA_CHANNEL_ID_SUFFIX}`
-const debug = false
-const sora = Sora.connection(SORA_SIGNALING_URL, debug)
-const metadata = { access_token: ACCESS_TOKEN }
-const options = {}
-
-const sendrecv1 = sora.sendrecv(channelId, metadata, options)
-
-sendrecv1.on('notify', (event) => {
-  if (event.event_type === 'connection.created' && sendrecv1.connectionId === event.connection_id) {
-    const connectionIdElement = document.querySelector('#sendrecv1-connection-id')
-    connectionIdElement.textContent = event.connection_id
-  }
-})
-
-sendrecv1.on('track', (event) => {
-  const stream = event.streams[0]
-  if (!stream) return
-  const remoteVideoId = `sendrecv1-remotevideo-${stream.id}`
-  const remoteVideos = document.querySelector('#sendrecv1-remote-videos')
-  if (!remoteVideos.querySelector(`#${remoteVideoId}`)) {
-    const remoteVideo = document.createElement('video')
-    remoteVideo.id = remoteVideoId
-    remoteVideo.style.border = '1px solid red'
-    remoteVideo.autoplay = true
-    remoteVideo.playsinline = true
-    remoteVideo.controls = true
-    remoteVideo.width = '160'
-    remoteVideo.height = '120'
-    remoteVideo.srcObject = stream
-    remoteVideos.appendChild(remoteVideo)
-  }
-})
-
-sendrecv1.on('removetrack', (event) => {
-  const remoteVideo = document.querySelector(`#sendrecv1-remotevideo-${event.target.id}`)
-  if (remoteVideo) {
-    document.querySelector('#sendrecv1-remote-videos').removeChild(remoteVideo)
-  }
-})
-
-const sendrecv2 = sora.sendrecv(channelId, metadata, options)
-
-sendrecv2.on('notify', (event) => {
-  if (event.event_type === 'connection.created' && sendrecv2.connectionId === event.connection_id) {
-    const connectionIdElement = document.querySelector('#sendrecv2-connection-id')
-    connectionIdElement.textContent = event.connection_id
-  }
-})
-
-sendrecv2.on('track', (event) => {
-  const stream = event.streams[0]
-  if (!stream) return
-  const remoteVideoId = `sendrecv2-remotevideo-${stream.id}`
-  const remoteVideos = document.querySelector('#sendrecv2-remote-videos')
-  if (!remoteVideos.querySelector(`#${remoteVideoId}`)) {
-    const remoteVideo = document.createElement('video')
-    remoteVideo.id = remoteVideoId
-    remoteVideo.style.border = '1px solid red'
-    remoteVideo.autoplay = true
-    remoteVideo.playsinline = true
-    remoteVideo.controls = true
-    remoteVideo.width = '160'
-    remoteVideo.height = '120'
-    remoteVideo.srcObject = stream
-    remoteVideos.appendChild(remoteVideo)
-  }
-})
-
-sendrecv2.on('removetrack', (event) => {
-  const remoteVideo = document.querySelector(`#sendrecv2-remotevideo-${event.target.id}`)
-  if (remoteVideo) {
-    document.querySelector('#sendrecv2-remote-videos').removeChild(remoteVideo)
-  }
-})
-
-document.querySelector('#start-sendrecv1').addEventListener('click', async () => {
-  // sendrecv1
-  const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true })
-  await sendrecv1.connect(mediaStream)
-  document.querySelector('#sendrecv1-local-video').srcObject = mediaStream
-})
-
-document.querySelector('#start-sendrecv2').addEventListener('click', async () => {
-  // sendrecv2
-  const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true })
-  await sendrecv2.connect(mediaStream)
-  document.querySelector('#sendrecv2-local-video').srcObject = mediaStream
-})
-
-document.querySelector('#stop-sendrecv1').addEventListener('click', async () => {
-  await sendrecv1.disconnect()
-  document.querySelector('#sendrecv1-local-video').srcObject = null
-  document.querySelector('#sendrecv1-remote-videos').innerHTML = null
-})
-
-document.querySelector('#stop-sendrecv2').addEventListener('click', async () => {
-  await sendrecv2.disconnect()
-  document.querySelector('#sendrecv2-local-video').srcObject = null
-  document.querySelector('#sendrecv2-remote-videos').innerHTML = null
-})
diff --git a/examples/sendrecv/main.mts b/examples/sendrecv/main.mts
new file mode 100644
index 00000000..4b54bf74
--- /dev/null
+++ b/examples/sendrecv/main.mts
@@ -0,0 +1,142 @@
+import Sora, {
+  type SoraConnection,
+  type SignalingNotifyMessage,
+  ConnectionPublisher,
+} from '../../dist/sora'
+
+document.addEventListener('DOMContentLoaded', async () => {
+  const SORA_SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL
+  const SORA_CHANNEL_ID_PREFIX = import.meta.env.VITE_SORA_CHANNEL_ID_PREFIX || ''
+  const SORA_CHANNEL_ID_SUFFIX = import.meta.env.VITE_SORA_CHANNEL_ID_SUFFIX || ''
+  const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN || ''
+
+  const sendrecv1 = new SoraClient(
+    'sendrecv1',
+    SORA_SIGNALING_URL,
+    SORA_CHANNEL_ID_PREFIX,
+    SORA_CHANNEL_ID_SUFFIX,
+    ACCESS_TOKEN,
+  )
+
+  const sendrecv2 = new SoraClient(
+    'sendrecv2',
+    SORA_SIGNALING_URL,
+    SORA_CHANNEL_ID_PREFIX,
+    SORA_CHANNEL_ID_SUFFIX,
+    ACCESS_TOKEN,
+  )
+
+  document.querySelector('#sendrecv1-start')?.addEventListener('click', async () => {
+    // sendrecv1
+    const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true })
+    await sendrecv1.connect(stream)
+  })
+  document.querySelector('#sendrecv1-stop')?.addEventListener('click', async () => {
+    await sendrecv1.disconnect()
+  })
+
+  document.querySelector('#sendrecv2-start')?.addEventListener('click', async () => {
+    // sendrecv2
+    const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true })
+    await sendrecv2.connect(stream)
+  })
+  document.querySelector('#sendrecv2-stop')?.addEventListener('click', async () => {
+    await sendrecv2.disconnect()
+  })
+})
+
+class SoraClient {
+  // sendrecv1 or sendrecv2
+  private label: string
+
+  private debug = false
+
+  private channelId: string
+  private metadata: { access_token: string }
+  private options: object
+
+  private sora: SoraConnection
+  private connection: ConnectionPublisher
+
+  constructor(
+    label: string,
+    signalingUrl: string,
+    channelIdPrefix: string,
+    channelIdSuffix: string,
+    accessToken: string,
+  ) {
+    this.label = label
+
+    this.sora = Sora.connection(signalingUrl, this.debug)
+    this.channelId = `${channelIdPrefix}sendrecv${channelIdSuffix}`
+    this.metadata = { access_token: accessToken }
+    this.options = {}
+
+    this.connection = this.sora.sendrecv(this.channelId, this.metadata, this.options)
+
+    this.connection.on('notify', this.onnotify.bind(this))
+    this.connection.on('track', this.ontrack.bind(this))
+    this.connection.on('removetrack', this.onremovetrack.bind(this))
+  }
+
+  async connect(stream: MediaStream) {
+    await this.connection.connect(stream)
+    const localVideo = document.querySelector<HTMLVideoElement>(`#${this.label}-local-video`)
+    if (localVideo) {
+      localVideo.srcObject = stream
+    }
+  }
+
+  async disconnect() {
+    await this.connection.disconnect()
+
+    // お掃除
+    const localVideo = document.querySelector<HTMLVideoElement>(`#${this.label}-local-video`)
+    if (localVideo) {
+      localVideo.srcObject = null
+    }
+    // お掃除
+    const remoteVideos = document.querySelector(`#${this.label}-remote-videos`)
+    if (remoteVideos) {
+      remoteVideos.innerHTML = ''
+    }
+  }
+
+  private onnotify(event: SignalingNotifyMessage): void {
+    if (
+      event.event_type === 'connection.created' &&
+      this.connection.connectionId === event.connection_id
+    ) {
+      const connectionIdElement = document.querySelector(`#${this.label}-connection-id`)
+      if (connectionIdElement) {
+        connectionIdElement.textContent = event.connection_id
+      }
+    }
+  }
+
+  private ontrack(event: RTCTrackEvent): void {
+    const stream = event.streams[0]
+    const remoteVideoId = `${this.label}-remotevideo-${stream.id}`
+    const remoteVideos = document.querySelector(`#${this.label}-remote-videos`)
+    if (remoteVideos && !remoteVideos.querySelector(`#${remoteVideoId}`)) {
+      const remoteVideo = document.createElement('video')
+      remoteVideo.id = remoteVideoId
+      remoteVideo.style.border = '1px solid red'
+      remoteVideo.autoplay = true
+      remoteVideo.playsInline = true
+      remoteVideo.controls = true
+      remoteVideo.width = 160
+      remoteVideo.height = 120
+      remoteVideo.srcObject = stream
+      remoteVideos.appendChild(remoteVideo)
+    }
+  }
+
+  private onremovetrack(event: MediaStreamTrackEvent): void {
+    const target = event.target as MediaStream
+    const remoteVideo = document.querySelector(`#${this.label}-remotevideo-${target.id}`)
+    if (remoteVideo) {
+      document.querySelector(`#${this.label}-remote-videos`)?.removeChild(remoteVideo)
+    }
+  }
+}
diff --git a/tests/sendrecv.spec.ts b/tests/sendrecv.spec.ts
index 1116ebd3..d8e62447 100644
--- a/tests/sendrecv.spec.ts
+++ b/tests/sendrecv.spec.ts
@@ -3,8 +3,8 @@ import { test } from '@playwright/test'
 test('sendrecv x2', async ({ page }) => {
   await page.goto('http://localhost:9000/sendrecv/')
 
-  await page.click('#start-sendrecv1')
-  await page.click('#start-sendrecv2')
+  await page.click('#sendrecv1-start')
+  await page.click('#sendrecv2-start')
 
   // #sendrecv1-connection-id 要素が存在し、その内容が空でないことを確認するまで待つ
   await page.waitForSelector('#sendrecv1-connection-id:not(:empty)')
@@ -20,6 +20,6 @@ test('sendrecv x2', async ({ page }) => {
   const sendrecv2ConnectionId = await page.$eval('#sendrecv2-connection-id', (el) => el.textContent)
   console.log(`sendrecv2 connectionId=${sendrecv2ConnectionId}`)
 
-  await page.click('#stop-sendrecv1')
-  await page.click('#stop-sendrecv2')
+  await page.click('#sendrecv1-stop')
+  await page.click('#sendrecv2-stop')
 })