ZOOM Meeting SDKで字幕流したかった


## 準備

アカウント周り
https://qiita.com/yosuke-sawamura/items/de69e73e47335cd61d68

## 以下ソースを使っていく

https://qiita.com/yosuke-sawamura/items/df6eb3b4bc2f11ba281f

準備ができたらミーティングに参加していくぅ

最初meeting numberにスペースがあって参加できなかったので注意

![スクリーンショット 2022-06-22 12.22.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329559/b92f58a1-b9d2-0abd-de99-01c2d0d2c4ee.png)

コピーだけでzoomがうごくなんて・・・すごエンジニアになった気分になれます。自慢しましょう


![スクリーンショット 2022-06-22 12.47.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329559/38266cf8-a40a-5a6e-1bf0-885ab7702ae9.png)

## chatが来たときのリスナを探す

web-sdkのリファレンス見る

https://marketplace.zoom.us/docs/sdk/native-sdks/web/client-view/reference

inMeetingServiceListenerが怪しいけどEvent定義が4つしかない？

結局chat周りのAPIは探せませんでした

## 執念でやる

websocketをオーバーライドして自前でやればできるかも？と考えたが怒られそうなのでやめた

DOM監視でやることにした

このポップアップと

![スクリーンショット 2022-06-22 12.48.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329559/697e6f84-e97b-5e1b-4314-72cc9651f527.png)

このチャット部分から取ろう(reactで操作していると思うので非表示時にはここからはとれない）

![スクリーンショット 2022-06-22 12.48.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329559/3c8028b2-4221-1b8f-1169-d098fb4c39a5.png)

## ソース追加

public/index.htmlにcss追加
```css
@keyframes scroll{
    from { right: -100%}
    to { right: 100%}
}
.jimaku {
    animation : scroll 5s linear;
    position: absolute;
    top: 50%;
    font-size: 5em;
    word-break: keep-all;
}
```

public/js/index.jsにソース追加
```js
/** @param {string} text */
function createJimaku(text) {
  const p = document.createElement('p')
  p.classList.add('jimaku')
  p.innerText = text
  const random = Math.random() * (80 - 10) + 10
  p.style.top = random + '%'
  const color = ['red', 'white', 'blue', 'green']
  p.style.color = color[Math.floor(Math.random() * color.length)]
  p.addEventListener('animationend', () => { p.remove() })
  document.body.append(p)
}

function startObserve() {
  let chatObserver;
  // 画面右のチャットタブからコメントを取得する場合
  const containerObserver = new MutationObserver(records => {
    const record = records[0]
    if (record.addedNodes.length) {
      const container = record.addedNodes[0]
      const list = container.querySelector('#chat-list-content')
      chatObserver = new MutationObserver(records => {
        records.forEach(record => {
          if (!record.addedNodes.length) { return }
          record.addedNodes.forEach(el => {
            const textEl = el.querySelector('.chat-message-text-content')
            if (textEl && textEl.id) {
              createJimaku(el.querySelector('.chat-message-text-content').innerText);
            }
          })
        })
      })
      chatObserver.observe(list, { childList: true , subtree: true})
    }
    if (record.removedNodes.length) {
      chatObserver.disconnect()
    }
  })
  const container = document.getElementById('wc-content')
  containerObserver.observe(container, {
    childList: true
  })

  // 通知から取る場合
  const chatIcon = Array.from(document.querySelectorAll('.footer-button-base__button-label')).find(el => el.innerText === 'Chat')
  const chatButton = chatIcon.closest('.footer-button__wrapper')
  const chatPopObserver = new MutationObserver(records => {
    if (chatObserver) { return }
    records.forEach(record => {
      // 文字だけがupdateされた場合（連投）
      if (record.type === 'characterData' && !record.target.parentElement.classList.contains('number-badge')) {
        createJimaku(record.target.data)
        return
      }
      if (!record.addedNodes.length) { return; }
      // ポップアップ表示から取得
      record.addedNodes.forEach(el => {
        const textEl = el.querySelector('.last-chat-message-tip__container > div:last-child')
        if (textEl) {
          createJimaku(textEl.innerText);
        }
      })
    })
  })
  chatPopObserver.observe(chatButton, {childList: true, subtree: true, characterData: true})
}

```

作成したstartObserveをZoomMtg.joinのsuccessコールバックで呼び出す
```js
            ZoomMtg.join({
              meetingNumber: meetingid,
              userName: name,
              signature: signature,
              sdkKey: sdkKey,
              passWord: meetingpwd,
              success: function (res) {
                console.log("join meeting success", res);
                startObserve()
              },
              error: function (res) {
                console.log(res);
              },
            });
```

## いざ、実食

画面下部のポップアップから取得した場合（長いコメントは途切れる）

![CPT2206221238-1424x773 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329559/7969a9e6-215c-739b-3e77-00c3f61cfb82.gif)

チャットタブから取得した場合
![CPT2206221241-1414x776 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329559/54d3d75a-055d-0c39-cbec-495db0246899.gif)

自分は何をやっているんだろう

これでchatのリスナあったら照れちゃう(〃ω〃)

## おわり

HTMLの構造変わるだけで動かなくなるので真似しないでください

バグもあると思います

他にも楽しそうなAPIがい〜っぱい！Zoom API/SDKでみんなたのしもう!

https://qiita.com/official-events/86d8555146e1b14fef24#:~:text=https%3A//qiita.com/official%2Dcampaigns/engineer%2Dfesta/2022