6
3

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.

この記事誰得? 私しか得しないニッチな技術で記事投稿!

OBSで開始したYoutube Liveの配信URLをLINE公式アカウントで通知する(M1 Mac)

Last updated at Posted at 2023-06-16

はじめに

Youtube Liveを限定公開で配信する場合、チャンネルのSubscriberに配信URLが通知されない。
また、Youtube LiveとTwitterの連携が2019年で終了したようなので、Twitterでの通知も不可である。
Lumia Streamは配信開始を検知して好きなスクリプトを走らせたりAPIを叩いたりできるが、配信URLを収集していない(とDiscordで言われた)。従って配信URLを取得するスクリプトを書いた。
LINE通知については、Subscriberと主にLINEで繋がっているため選ばれただけで、おまけ程度である。

システム概要

image832.png

  1. OBSにて配信を開始するとOBS scriptがそれを検知
  2. Python scriptを呼び出して配信URLをスクレイピングしOBS scriptに返す
  3. 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通知を行うためのスクリプトを作成するために、下記を参考にした。

"script_xxx"系の名前のfunctionについては下記に説明がある。

OBSのイベントに関する定数は下記に説明がある。

作成したスクリプト:

obsexec.lua
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が起動中の場合はスクリプトが落ちる。その対策は下記の通り:

作成したスクリプト:

getStreamingURL.py
# 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を受け取るので一部改変している。

postMsgToLINE_start.command
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
6
3
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
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?