4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【SHOWROOM】配信中のコメント・ギフトログをリアルタイムに取得する方法

Last updated at Posted at 2021-02-12

はじめに

最近SHOWROOMを見始めて、バーチャル枠を見てると配信画面にコメントやらギフトログを出している人が居るのでそれってどうやって取得できるのかな?と思ったのがこの記事を書くことになった切っ掛けです

調査

双方向通信って事はWebSocketでも使っているのかな?
と言う事で配信画面でブラウザの開発者ツール開いてWSを見てみると案の定
online.showroom-live.comSUB 何かの文字列を送ることで配信内のコメントなど取得できるようになってるみたいです
ws.png

謎の文字列

WebSocket使っているのは分かったが、接続した後何の文字列を送っているのか?
色々なルームの文字列を見たところbXXXXX(6文字):XXXXXXXX(8文字)の形式みたいです

色々調べてみるとAPIがあるみたいでまとめてあるページがあったので参考に調べてみたところ
https://www.showroom-live.com/api/live/onlives
にアクセスした際各ルームのbcsvr_keyと言う所の値がそれっぽいみたいです

bcsvrが何なのか調べてみたらDeNAが開発したギフティングやコメント向けのリアルタイムサーバみたいです

接続してみる

ささっとJavaScriptでコンソールに出力して中身を見てみます

// WebSocket 接続を作成
const socket = new WebSocket('wss://online.showroom-live.com')

// 接続が開いたときのイベント
socket.onopen = (e) => {
    socket.send('SUB\tbXXXXX:XXXXXXXX')
}

// メッセージの待ち受け
socket.onmessage = (message) => {
    console.log(message.data)
}

コンソールに出力される内容を見てみると、基本的に下記のような形式で取得されているみたいです

// コメント
MSG	bXXXXX:XXXXXXXX	{"ua":値,"av":値,"d":値,"ac":"値","cm":"値","created_at":値,"u":値,"at":値,"t":"値"}
// ギフト
MSG	bXXXXX:XXXXXXXX	{"ua":値,"n":値,"av":値,"d":値,"ac":"値","created_at":値,"u":値,"h":値,"g":値,"gt":値,"at":値,"t":"値"}
// テロップ
MSG	bXXXXX:XXXXXXXX	{"telops":[{"color":{"r":値,"b":値,"g":値},"text":"値","type":"値"},{"color":{"r":値,"b":値,"g":値},"text":"値","type":"値"}],"telop":"値","interval":値,"t":値,"api":"値"}
// 疎通確認
ACK showroom
// 不明
MSG bXXXXX:XXXXXXXX {"created_at":値,"t":値}
MSG bXXXXX:XXXXXXXX {"created_at":値,"u":値,"at":値,"t":値}
// エラー(海外配信者のルームでよく見かける)
Could not decode a text frame as UTF-8.

疎通確認時とエラーのテキスト以外、最初のMSG bXXXXX:XXXXXXXX を除いた後の文字列はJSONだと思われるのでJSONに変換したら色々加工できると推測

JavaScriptでJSONオブジェクトとして使えるように加工

// (略)
// メッセージの待ち受け
socket.onmessage = (message) => {
    if (message.data === "ACK\tshowroom" || message.data === "Could not decode a text frame as UTF-8.") {
        console.log("疎通確認又はエラー")
    } else {
        // JSONオブジェクトとして取得
        console.log(JSON.parse(message.data.replace("MSG\tbXXXXX:XXXXXXXX", "")))
    }
}

コメントログとギフトログを取得

コンソールのログを見てみるとコメントログの要素数が9、ギフトログの要素数が12と言う事が分かっています
なのでJSONに変換した後、要素数で分けてあげればコメントログとギフトログに分ける事ができます

// (略)
// メッセージの待ち受け
socket.onmessage = (message) => {
    if (message.data === "ACK\tshowroom" || message.data === "Could not decode a text frame as UTF-8.") {
        console.log("疎通確認はエラー")
    } else {
        // JSONオブジェクトとして取得
        let messageJson = JSON.parse(message.data.replace("MSG\tbXXXXX:XXXXXXXX", ""))
        if (Object.keys(messageJson).length === 9) {
            // コメントログ
        } else if (Object.keys(messageJson).length === 12) {
            // ギフトログ
        } else {
            // テロップなど(今回は無視)
        }
    }
}

JSON要素の構成

コメント

{
    ac: "ユーザ名",
    at: 0(不明、固定っぽい?),
    av: アバターID,
    cm: "コメント",
    created_at: 投稿時間,
    d: 0(不明、固定っぽい?),
    t: "1"(不明、固定っぽい?1がコメント?),
    u: ユーザID,
    ua: リピーター(3)、初見(2)、ビギナー(1),
}

ギフト

{
    ac: "ユーザ名",
    at: 0(不明、固定っぽい?),
    av: アバターID,
    created_at: 投稿時間,
    d: 0(不明、固定っぽい?),
    g: ギフトID,
    gt: 無料(1)、有料(2),
    h: 0(不明),
    n: ギフトの個数,
    t: "2"(不明、固定っぽい?2がギフト?),
    u: ユーザID,
    ua: リピーター(3)、初見(2)、ビギナー(1),
}

画面にログを出してみる

画面を準備

<div>
    <div id="comment">
        <!-- コメントログ -->
    </div>
    <div id="gift">
        <!-- ギフトログ -->
    </div>
</div>

JavaScriptも少し修正しておきます

// (略)
// メッセージの待ち受け
socket.onmessage = (message) => {
    if (message.data === "ACK\tshowroom" || message.data === "Could not decode a text frame as UTF-8.") {
        console.log("疎通確認又はエラー")
    } else {
        // JSONオブジェクトとして取得
        let messageJson = JSON.parse(message.data.replace("MSG\tbXXXXX:XXXXXXXX", ""))
        if (Object.keys(messageJson).length === 9) {
            // コメントログ
            showComment(messageJson) // 追加
        } else if (Object.keys(messageJson).length === 12) {
            // ギフトログ
            showGift(messageJson) // 追加
        } else {
            // テロップなど(今回は無視)
        }
    }
}

コメント

表示に必要な項目は

  • ユーザ名
  • アバターID
  • コメント

の3つがあれば十分なのでコメントに関してはこの3項目を使用

function showComment(comment) {
    // アバター画像要素
    let avaElement = document.createElement('img')
    avaElement.src = "https://image.showroom-cdn.com/showroom-prod/image/avatar/" + comment.av + ".png"
    avaElement.width = 40
    avaElement.height = 40
    // コメント要素
    let commentElement = document.createElement('p')
    commentElement.textContent = comment.ac + ":" + comment.cm
    // アバターとコメントを纏める
    let divElement = document.createElement('div')
    divElement.appendChild(avaElement)
    divElement.appendChild(commentElement)
    // 画面に表示
    document.getElementById('comment').appendChild(divElement)
}

ギフト

表示に必要な項目は

  • ユーザ名
  • アバターID
  • ギフトID
  • ギフト個数

の4つがあれば十分なのでギフトに関してはこの4項目を使用

function showGift(gift) {
    // アバター画像要素
    let avaElement = document.createElement('img')
    avaElement.src = "https://image.showroom-cdn.com/showroom-prod/image/avatar/" + gift.av + ".png"
    avaElement.width = 40
    avaElement.height = 40
    // ギフト画像要素
    let gitfElement = document.createElement('img')
    gitfElement.src = "https://image.showroom-cdn.com/showroom-prod/assets/img/gift/" + gift.g + "_s.png"
    gitfElement.width = 20
    gitfElement.height = 20
    // ギフト個数要素
    let commentElement = document.createElement('p')
    commentElement.textContent = gift.ac + ":" + gift.n + ""
    // アバターとギフトを纏める
    let divElement = document.createElement('div')
    divElement.appendChild(avaElement)
    divElement.appendChild(gitfElement)
    divElement.appendChild(commentElement)
    // 画面に表示
    document.getElementById('gift').appendChild(divElement)
}

完成形

ソースの全体像は下記を参照してください

<!doctype html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <title>SRコメント、ギフト取得</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
    <div>
        <div id="comment"></div>
        <div id="gift"></div>
    </div>

    <script>
        window.onload = function () {
            // WebSocket 接続を作成
            const socket = new WebSocket('wss://online.showroom-live.com')

            // 接続が開いたときのイベント
            socket.onopen = (e) => {
                socket.send('SUB\tbXXXXX:XXXXXXXX')
            }

            // メッセージの待ち受け
            socket.onmessage = (message) => {
                if (message.data === "ACK\tshowroom" || message.data === "Could not decode a text frame as UTF-8.") {
                    console.log("疎通確認又はエラー")
                } else {
                    // JSONオブジェクトとして取得
                    let messageJson = JSON.parse(message.data.replace("MSG\tbXXXXX:XXXXXXXX", ""))
                    if (Object.keys(messageJson).length === 9) {
                        // コメントログ
                        showComment(messageJson)
                    } else if (Object.keys(messageJson).length === 12) {
                        // ギフトログ
                        showGift(messageJson)
                    } else {
                        // テロップなど(今回は無視)
                    }
                }
            }
        }

        function showComment(comment) {
            // アバター画像要素
            let avaElement = document.createElement('img')
            avaElement.src = "https://image.showroom-cdn.com/showroom-prod/image/avatar/" + comment.av + ".png"
            avaElement.width = 40
            avaElement.height = 40
            // コメント要素
            let commentElement = document.createElement('p')
            commentElement.textContent = comment.ac + ":" + comment.cm
            // アバターとコメントを纏める
            let divElement = document.createElement('div')
            divElement.appendChild(avaElement)
            divElement.appendChild(commentElement)
            // 画面に表示
            document.getElementById('comment').appendChild(divElement)
        }

        function showGift(gift) {
            // アバター画像要素
            let avaElement = document.createElement('img')
            avaElement.src = "https://image.showroom-cdn.com/showroom-prod/image/avatar/" + gift.av + ".png"
            avaElement.width = 40
            avaElement.height = 40
            // ギフト画像要素
            let gitfElement = document.createElement('img')
            gitfElement.src = "https://image.showroom-cdn.com/showroom-prod/assets/img/gift/" + gift.g + "_s.png"
            gitfElement.width = 20
            gitfElement.height = 20
            // ギフト個数要素
            let commentElement = document.createElement('p')
            commentElement.textContent = gift.ac + ":" + gift.n + ""
            // アバターとギフトを纏める
            let divElement = document.createElement('div')
            divElement.appendChild(avaElement)
            divElement.appendChild(gitfElement)
            divElement.appendChild(commentElement)
            // 画面に表示
            document.getElementById('gift').appendChild(divElement)
        }
    </script>
</body>

</html>

最後に

後はお好みでCSSを適応させて見栄えよくさせればオリジナルのコメントビューワーを手軽に作成することができます!

4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?