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.準備
- Spoonのアカウントを作成する.
- AssistantSeikaを設定して、PowershellからVOICELOID2を実行できるようにする.
- WebDriverをpythonで扱えるようにしておく.
- 下記(6.)からpythonのソースを取得する.
5.使い方
- PowerShellで、メインファイルのフォルダへ移動し、
「python Spoon_Live_Yomiage」を実行する.
- 自動起動した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.webdriver.common.keys import Keys
import time
# 初期設定
# 基本情報
WEB_DRIVER = 'C:\web_driver\msedgedriver.exe' # ウェブドライバーのパスを入力する.
# 開くウィンドウの設定
options = EdgeOptions()
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.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.よくあるエラー
- WebDriverが最新ではない.または、バージョンがEdgeと一致していない.
→WebDriverとEdgeのバージョンを同じにしておく.
- フォルダやファイルの位置が一致していない.
→好きにファイルの位置を移動してもよいですが、そのパスをしっかりとファイルに反映させておいてください.