はじめに
Youtube Liveを限定公開で配信する場合、チャンネルのSubscriberに配信URLが通知されない。
また、Youtube LiveとTwitterの連携が2019年で終了したようなので、Twitterでの通知も不可である。
Lumia Streamは配信開始を検知して好きなスクリプトを走らせたりAPIを叩いたりできるが、配信URLを収集していない(とDiscordで言われた)。従って配信URLを取得するスクリプトを書いた。
LINE通知については、Subscriberと主にLINEで繋がっているため選ばれただけで、おまけ程度である。
システム概要
- OBSにて配信を開始するとOBS scriptがそれを検知
- Python scriptを呼び出して配信URLをスクレイピングしOBS scriptに返す
- URLをShell scriptに渡してLINE通知
開発環境
- M1 MacBook Air Ventura 13.4
- OBS Studio 29.1.1 (64bit)
- Python 3.9.6 64-bit
スクリプト説明
OBS script(Lua) で配信開始をトリガ
配信開始時にスクレイピングとLINE通知を行うためのスクリプトを作成するために、下記を参考にした。
OBSで配信開始時にツイート画面を表示するだけのスクリプトを書いてみました。初Luaなので動かなかったらすみません。https://t.co/C02zO7iKjx
— レモングラス (@Iemonglass) December 7, 2020
記入例 pic.twitter.com/CfvKDOV5ap
"script_xxx"系の名前のfunctionについては下記に説明がある。
OBSのイベントに関する定数は下記に説明がある。
作成したスクリプト:
obs = obslua
settings = {}
-- Script hook for defining the script description
function script_description()
return [[
配信開始と終了時にその旨を配信URLと一緒にLINEに通知するスクリプト。
ただし、LINEへの通知スクリプト(postMsgToLINE_start.command)と、配信URLを取得するスクリプト(getStreamingURL.py)は別ファイル
by maple0705
]]
end
-- Script hook for defining the settings that can be configured for the script
function script_properties()
local props = obs.obs_properties_create()
obs.obs_properties_add_text(props, "targetprofile", "対象プロファイル", obs.OBS_TEXT_DEFAULT)
obs.source_list_release(scenes)
return props
end
-- Script hook that is called whenver the script settings change
function script_update(_settings)
settings = _settings
end
-- Script hook that is called when the script is loaded
function script_load(settings)
obs.obs_frontend_add_event_callback(handle_event)
end
function handle_event(event)
if event == obs.OBS_FRONTEND_EVENT_STREAMING_STARTED then
obs.script_log(obs.LOG_INFO, "配信を開始しました。")
local currentprofile = obs.obs_frontend_get_current_profile()
local savedprofile = obs.obs_data_get_string(settings, "targetprofile")
if currentprofile == savedprofile then
local streaming_url = get_streaming_url()
obs.script_log(obs.LOG_INFO, streaming_url)
post_MsgToLINE_start(streaming_url)
end
elseif event == obs.OBS_FRONTEND_EVENT_STREAMING_STOPPED then
obs.script_log(obs.LOG_INFO, "配信を終了しました。")
local currentprofile = obs.obs_frontend_get_current_profile()
local savedprofile = obs.obs_data_get_string(settings, "targetprofile")
if currentprofile == savedprofile then
post_MsgToLINE_stop()
end
end
end
function get_streaming_url()
local streaming_url = ""
local python_path = "./getStreamingURL.py"
local command = "python3 " .. python_path
local handle = io.popen(command)
streaming_url = handle:read("*a")
handle:close()
return streaming_url
end
function post_MsgToLINE_start(streaming_url)
local command = "./postMsgToLINE_start.command " .. streaming_url
local handle = io.popen(command)
rep = handle:read("*a")
handle:close()
end
function post_MsgToLINE_stop()
local command = "./postMsgToLINE_stop.command "
local handle = io.popen(command)
rep = handle:read("*a")
handle:close()
end
Python script で配信URLを取得
スクリプトの骨格として下記を参考にした。
私の場合はSeleniumコンテナは使用せず、Youtubeにログイン済の自分のChromeを使用するので、そのChromeの起動方法は下記を参考にした("ソースコード"節)。
ただし、上記のコードでは、既にChromeが起動中の場合はスクリプトが落ちる。その対策は下記の通り:
作成したスクリプト:
# execute below on terminal first:
# "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --remote-debugging-port=9222
# and "lsof -i:9222" on terminal if you want to check
from selenium import webdriver
from selenium.webdriver.chrome import service as ChromeService
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from bs4 import BeautifulSoup
try:
options = Options()
options.add_argument("--user-data-dir=/Users/maple0705/Library/Application Support/Google/Chrome")
options.add_argument("--profile-directory=Default")
options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")
#options.add_argument("--headless")
# chromeを起動
chrome_service = ChromeService.Service(executable_path="/usr/local/bin/chromedriver")
driver = webdriver.Chrome(service=chrome_service, options=options)
# open new window
driver.execute_script("window.open()")
new_window = driver.window_handles[-1]
driver.switch_to.window(new_window)
# 自分の配信の管理画面のURLを開く
driver.get("https://studio.youtube.com/channel/xxxxx/livestreaming/manage")
# 定義済みの条件で待機する(最大20秒)
WebDriverWait(driver, 20).until(
# 指定したページ上の要素が読み込まれるまで待機
EC.presence_of_element_located((By.TAG_NAME, "ytcp-video-row"))
)
# HTMLを文字コードをUTF-8に変換してから取得
html = driver.page_source.encode('utf-8')
# 取得したhtmlをBeautifulSoupで解析
soup = BeautifulSoup(html, "html.parser")
# 配信一覧を表示している要素だけに絞る
section = soup.find("div", id="scroll-container", class_="style-scope ytls-broadcast-list-content")
# 配信中ライブすべてを抽出する
rowlist = section.find_all("div", id="row-container")
# 配信中ライブのURL
# for row in rowlist:
# elem = row.find(id="video-title")
# url = elem.get("href")
# print(url)
# 最新ライブのurl
elem = rowlist[0].find(id="video-title")
url = "https://youtube.com" + elem.get("href")
url = url.replace("/video", "/watch")
url = url.replace("/livestreaming", "")
print(url)
except Exception as e:
print("**error**")
print(e)
finally:
# 終了
driver.close() # close tab of chrome
driver.quit()
Shell script でLINE公式アカウントからURLを通知
LINE Developers のAPIリファレンスにスクリプト例があるのでそれを拝借した。スクレイピングで取得したURLを受け取るので一部改変している。
curl -v -X POST https://api.line.me/v2/bot/message/broadcast \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {channel access token}' \
-d @- <<EOS
{
"messages":[
{
"type":"text",
"text":"配信を開始しました!見てね!"
},
{
"type":"text",
"text":"${1}"
}
]
}
EOS