Spoonの読みあげ機能

1.はじめに

   Spoonの読みあげ機能についてですが、現状(2022/11/20)そういったツールはありません.ですので、PythonとAssistantSeikaを使って、読み上げツールを作ってみました.

   基本的に下記コードはご自由に使ってもらって構いませんが、コードを丸々流用して、製品化等はお控えください.

   また、何か不明な点や不具合があればコメント等お願いします.なるべく早く対応いたします.

 

2.目的

   Spoon配信中のコメント等をVOICELOID2(ついなちゃん)に読みあげてもらう.

   ※VOICELOID2なら何でもOK

 

3.環境

 ・PC

 ・PowerShell (7.3.0)

 ・Python(3.10.7)

    ①selenium(3.141.4)

    ②webdriver-manager(3.8.3)

  ・Microsoft Edge(バージョン 107.0.1418.52 (公式ビルド) (64 ビット))

  ・msedgedriver(Edgeのバージョンに合わせる)

  ・VOICELOID2

  ・AssistantSeika

  

4.準備

  1. Spoonのアカウントを作成する.
  2. AssistantSeikaを設定して、PowershellからVOICELOID2を実行できるようにする.
  3. WebDriverをpythonで扱えるようにしておく.
  4. 下記(6.)からpythonのソースを取得する.

 

5.使い方

  1. PowerShellで、メインファイルのフォルダへ移動し、
    python    Spoon_Live_Yomiage」を実行する.
  2. 自動起動したEdgeのSpoonから通常通り配信を行う.
    ※Spoonのサイトが完全に立ち上がるまでは、マウスやキーボードは弄らないこと.

 

6.ソース一覧とディレクトリ(フォルダ)の分け方

  ・ファイルの配置の例

        C:\Spoon_Live
                       ①Spoon_Live_Yomiage(メインファイル)

                       \lib(ライブラリーフォルダ)

                             ②Get_Spoon_Text.py

                             ③Main_Control.py

                             ④Talk_Comment.py 

        C:\web_driver(インストールしたウェブドライバーを入れる)

 

①Spoon_Live_Yomiage(メインファイル)

# Spoon Liveで読み上げを行う
# メインファイル

import lib.Main_Control as SPOON_CONTROL
import time
import subprocess
from webdriver_manager.microsoft import EdgeChromiumDriverManager
from msedge.selenium_tools import EdgeOptions, Edge
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time

# 初期設定
# 基本情報
WEB_DRIVER = 'C:\web_driver\msedgedriver.exe' # ウェブドライバーのパスを入力する.

# 開くウィンドウの設定
options = EdgeOptions()
options.use_chromium = True
options.add_argument('--inprivate')
options.add_argument('--lang=ja')
options.add_argument('--window-size=2000,1000')


def main():
    # ブラウザ起動
    driver = Edge(executable_path=WEB_DRIVER, options=options)
    driver.get(URL)
    # 1秒待機
    time.sleep(1)

    # テキストの初期設定

    chat_list = [[0,0,0,'nothing','nothing']]
    control_command = ['/mode1']
    host = 'nothing'
    time.sleep (20)

    while True:
        time.sleep(10)
        SPOON_CONTROL.main_control(driver, chat_list, control_command, host)

    driver.quit()

if __name__ == "__main__":
    main()

 

②Get_Spoon_Text.py

# テキストの抽出ファイル

from msedge.selenium_tools import Edge, EdgeOptions
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException # 例外処理用のライブラリ

# テキストの必要な部分だけを切り取る
def cut_text(Chat, Id_char):
    try:
        web_text = Chat.find_element(By.CSS_SELECTOR, Id_char)
        return web_text.get_attribute('textContent')
    except NoSuchElementException:
        return 'nothing'

# テキストの種類分け
def classify_text(Web_chat_list, Chat_list, Host):
   
    count = 0 # カウンター
    HOST = 'DJ' # 枠主の証
    name = 'nothing' # コメント主の名前

    for chat in Web_chat_list:
        text_type = chat.get_attribute('class') # テキストの種類を判別
       
        # ガイド(Spoonから)の処理
        if text_type == 'chat-list-item guide':
            # テキストの種類
            Chat_list[count][0] = 1
           
            # コメントの処理
        elif text_type == 'chat-list-item message':
            # テキストの種類
            Chat_list[count][0] = 2
            # 主のコメントか判断
            Host = cut_text(chat, '.badge')
            if HOST == Host:
                Chat_list[count][2] = 1
            else:
                pass
            # コメント主の名前
            name = cut_text(chat, '.name')        
            Chat_list[count][3] = name
            # コメント
            Chat_list[count][4] = cut_text(chat, '.comment-text')

        elif text_type == 'chat-list-item combo':
            # テキストの種類
            Chat_list[count][0] = 3
            # 主のコメントか判断
            if HOST == Host:
                Chat_list[count][2] = 1
            else:
                pass
            # コメント主の名前
            Chat_list[count][3] = name
            # コメント
            Chat_list[count][4] = cut_text(chat, '.comment-text')

        # 入室の処理
        elif text_type == 'chat-list-item enter':
            # テキストの種類
            Chat_list[count][0] = 4
            # コメント主の名前
            name = cut_text(chat, '.comment-text')        
            name = name.rstrip('さんが入室したよ')
            Chat_list[count][3] = name
       
        # ハートの処理
        elif text_type == 'chat-list-item like':
            # テキストの種類
            Chat_list[count][0] = 5
            # コメント主の名前
            name = cut_text(chat, 'p')        
            name = name.rstrip('さんがハートを押したよ!')
            Chat_list[count][3] = name

        # プレゼント(Spoon)の処理
        elif text_type == 'chat-list-item present':
            # テキストの種類
            Chat_list[count][0] = 6
            # コメント主の名前
            name = cut_text(chat, '.comment-text')        
            Chat_list[count][3] = name

        # 何もないときの処理
        else:
            pass
       
        count += 1

    return None

# テキスト抽出
def get_text(Driver, Chat_list, Host):
    # チャットリストを抽出する
    web_chat_list = Driver.find_elements(By.CSS_SELECTOR, '.chat-list-item')
   
    # Chat_listのサイズ確認:容量が足りなければ追加してやる
    Size = len(Chat_list)
    size = len(web_chat_list)
    if size > Size:
        # 差分を取って、その分配列を追加する。
        diff = size - Size
        for i in range(diff):
            Chat_list.append([0,0,0,'nothing','nothing'])
    else:
       pass
   
    # テキストの種類分け
    classify_text(web_chat_list, Chat_list, Host)

    return None

 

③Main_Control.py

import pandas as pd
import lib.Talk_Comment as Talk
import lib.Get_Spoon_Text as Get_Text

def main_control(Driver, Chat_list, Control_Command, Host):

    # コメント抽出
    Get_Text.get_text(Driver, Chat_list, Host)

    # コメントを発話させる
    if '/mode1' == Control_Command[0]:
        Talk.call_mode1(Chat_list, Control_Command)

    elif '/mode2' == Control_Command[0]:
        Talk.call_mode2(Chat_list, Control_Command)
   
    else:
        pass

    return None

 

Talk_Comment.py

※ subprocess.run("SeikaSay2.exe -cid 2009 -t \"{msg}\"".format(msg=Msg_Comment)

     \"ここに書いた文章が読み上げられる."\

     {msg}は、コメントを打った人の名前だと思ってください.

# 読みあげの設定ファイル

import subprocess

# モード変更する
def mode_change(mode, Control_Command):
    if '/mode1' == mode:
        Control_Command[0] = mode
        # 好きなコメント読みあげコメントを書いてね.
        subprocess.run("SeikaSay2.exe -cid 2009 -t \"コメントを読みあげを開始します.\"")

    elif '/mode2' == mode:
        Control_Command[0] = mode
        # 好きなコメント読みあげコメントを書いてね.
        subprocess.run("SeikaSay2.exe -cid 2009 -t \"コメントの読みあげを終了します.\"")
   
    else:
        pass
   
    return None

# コメント読みあげモード
def call_mode1(Chat_list, Control_Command):
    for i in range(len(Chat_list)):
       
        # 読みあげにチェックあるのは、先にスルーする
        if 0 == Chat_list[i][1]:
           
            # ガイド(Spoonから)の処理
            if 1 == Chat_list[i][0]:
                Chat_list[i][1] = 1 # 読みあげチェック
               
            # コメントがある場合
            elif 2 == Chat_list[i][0]:
                Chat_list[i][1] = 1 # 読みあげチェック

                # 主人からのコメントの場合
                if 1 == Chat_list[i][2]:
                    mode_change(Chat_list[i][4], Control_Command)
                    break

                # 主人以外からのコメントの場合    
                else:
                    Msg_Name = Chat_list[i][3]
                    Msg_Comment =  Chat_list[i][4]
                    # 与えられたコメントを発話させる
                    # 連続コメントは名前を読みあげない
                    if(Msg_Name != Chat_list[i-1]):
                        # 好きなコメント読みあげコメントを書いてね.
                        subprocess.run("SeikaSay2.exe -cid 2009 -t \"{msg}さんからコメントがきました.\"".format(msg=Msg_Name))
                    else:
                        pass

                    subprocess.run("SeikaSay2.exe -cid 2009 -t \"{msg}\"".format(msg=Msg_Comment))
           
            elif 3 == Chat_list[i][0]:
                Chat_list[i][1] = 1 # 読みあげチェック

                # 主人からのコメントの場合
                if 1 == Chat_list[i][2]:
                    mode_change(Chat_list[i][4], Control_Command)
                    break

                # 主人以外からのコメントの場合    
                else:
                    Msg_Name = Chat_list[i][3]
                    Msg_Comment =  Chat_list[i][4]
                    # 与えられたコメントを発話させる
                    subprocess.run("SeikaSay2.exe -cid 2009 -t \"{msg}\"".format(msg=Msg_Comment))
           
            # 入室を知らせる
            elif 4 == Chat_list[i][0]:
                Chat_list[i][1] = 1 # 読みあげチェック

                Msg_Name = Chat_list[i][3]
                # 与えられたコメントを発話させる
                # 好きなコメント読みあげコメントを書いてね.
                subprocess.run("SeikaSay2.exe -cid 2009 -t \"{msg}さんが入室しました。\"".format(msg=Msg_Name))
            # ハートを知らせる
            elif 5 == Chat_list[i][0]:
                Chat_list[i][1] = 1 # 読みあげチェック
               
                Msg_Name = Chat_list[i][3]
                # 与えられたコメントを発話させる
                # 好きなコメント読みあげコメントを書いてね.
                subprocess.run("SeikaSay2.exe -cid 2009 -t \"{msg}さんがハートをくれました。\"".format(msg=Msg_Name))

            # Spoonを知らせる
            elif 6 == Chat_list[i][0]:
                Chat_list[i][1] = 1 # 読みあげチェック
                   
                # 後々作る

            else:
                pass
        else:
            pass
    return  None

# 待機モード
def call_mode2(Chat_list, Control_Command):
    for i in range(len(Chat_list)):
       
        # 読みあげにチェックあるのは、先にスルーする
        if 0 == Chat_list[i][1]:
           
            # ガイド(Spoonから)の処理
            if 1 == Chat_list[i][0]:
                Chat_list[i][1] = 1 # 読みあげチェック
               
            # コメントがある場合
            elif 2 == Chat_list[i][0]:
                Chat_list[i][1] = 1 # 読みあげチェック

                # 主人からのコメントの場合
                if 1 == Chat_list[i][2]:
                    mode_change(Chat_list[i][4], Control_Command)
                    break

                # 主人以外からのコメントの場合    
                else:
                    pass

            elif 3 == Chat_list[i][0]:
                Chat_list[i][1] = 1 # 読みあげチェック

                # 主人からのコメントの場合
                if 1 == Chat_list[i][2]:
                    mode_change(Chat_list[i][4], Control_Command)
                    break

                # 主人以外からのコメントの場合    
                else:
                    pass

            else:
                Chat_list[i][1] = 1 # 読みあげチェック
        else:
            pass

    return None
 
7.よくあるエラー
  1. WebDriverが最新ではない.または、バージョンがEdgeと一致していない.
    →WebDriverとEdgeのバージョンを同じにしておく.
  2. フォルダやファイルの位置が一致していない.
    →好きにファイルの位置を移動してもよいですが、そのパスをしっかりとファイルに反映させておいてください.

☆自己紹介☆

みなさん

どうも、こんにちは(。ゝ∀・)ゞヨロシクゥ♪

 

永遠の二十歳こと、アメノハレです。

 

現在の職業は、SE・プログラマです。

でもゆくゆくは、ファーマーになる予定です。

 

趣味はいろいろあるのですが(プロフィールまで)

特に最近しているのは、

 

・バイクでツーリング

 

・アコギの練習

 

・アニメをみること

 

かな?( ゚ ρ ゚ )

 

日記やブログを書くのは初心者で、手探り状態での投稿ですが、温かい目で見守っていただければ嬉しいです(*´σー`)エヘヘ

 

主な投稿内容としては、

 

・休日の日記

 

・ハウツー(お酒の飲み方とか)

 

・挑戦記録(新たな資格取得の経過記録とか)

 

とかとか、投稿していければいいなぁっと思っています(≧▽≦)

 

最後に不定期ですが、SPOONで配信とかもやっているので良ければ聴きに来てね( `・∀・´)ノヨロシク

 

それでは、(*´∇`)ノシ マタネ~♪