はじめに
このサイトのメインテーマからは外れるのですが、歌詞をプロジェクタで映すのに我々は
OpenLPというソフトを使用しています。
この記事では、OpenLPのテキストをOBS Studioへオーバーレイする方法を紹介します。
OpenLPには遠隔操作のプラグインが組み込まれていて、JSONをHTTP経由でやり取りするプロトコルになっています。一方で、OBS Studioにはローカルに置かれたテキストファイルを読み込み、オーバーレイして表示する機能がついています。
この間に入って、OpenLPからテキストを取り出しローカルファイルに保存すれば、OpenLPのテキストをOBS Studioへ送ることが簡単にできます。
OpenLPの設定
遠隔操作プラグインを有効にしましょう。
もしOpenLPとOBS Studioが別のホスト上で動いている場合、OpenLPが動作しているホストのIPアドレスを固定しておくべきです。
さらに、ファイアウォールの4316番ポートを開けておく必要があります。
スクリプト
OpenLPとOBS Studioの間に入るスクリプトは、Pythonで書きます。
たったの160行です。
#! /bin/env python
# -*- coding: utf-8 -*-
import re
url_base = 'http://127.0.0.1:4316'
url_text = url_base + '/api/controller/live/text'
url_poll = url_base + '/api/poll'
fname_out = '/dev/shm/olp.txt'
fname_tmp = fname_out+'~'
n_maxchar = 50
flg_print = False
r_ascii = re.compile('[0-9a-zA-Z: ()]')
def my_count(s):
ret = 0
for c in s:
ret += 1 if r_ascii.match(c) else 2
return ret
def get_olp_text():
import urllib
import json
data = urllib.urlopen(url_text).read()
#print data
j = json.loads(data)
if flg_print: print j
for x in j['results']['slides']:
if 'img' in x:
return ''
if x['selected']:
return x['text']
return ''# return j['results']['slides'][0]['text']
def olp_poll():
import urllib
import json
j = json.loads(urllib.urlopen(url_poll).read())
if flg_print: print j
is_blank = j['results']['blank']
is_theme = j['results']['theme']
is_display = j['results']['display']
if is_blank or is_theme or is_display:
return False
return j
j_poll_prev = None
def get_olp():
global j_poll_prev
while True:
j = olp_poll()
if j != j_poll_prev:
j_poll_prev = j
break
from time import sleep
sleep(0.1)
if j:
return get_olp_text()
else:
return ''
def obs_output(data):
import codecs
with codecs.getwriter('utf8')(open(fname_tmp, 'w')) as f_tmp:
f_tmp.write(data)
import os
os.rename(fname_tmp, fname_out)
_r_nowrap = re.compile(u'[。、」]')
_r_alnum = re.compile(u'[A-Za-z0-9,.]')
_r_wrap = re.compile(u'[「]$')
def my_split(data, n_th):
data = re.sub(u"[ \t ]+", ' ', data)
line = u''
lines = []
n_line = 0
c_prev = ''
for c in data:
n = my_count(c)
if \
n_line + n <= n_th or \
_r_nowrap.match(c) or \
(_r_alnum.match(c) and _r_alnum.match(c_prev)) :
line += c; n_line += n
else:
t = _r_wrap.search(line)
if t:
t = t.start()
lines.append(line[0:t])
line = line[t:] + c
n_line = my_count(c)
else:
lines.append(line)
line = c; n_line = n
c_prev = c
if len(line):
lines.append(line)
return lines
def split_long_line(v, n):
v1 = []
for s in v:
v2 = my_split(s, n)
for t in v2:
v1.append(t)
return v1
def myfilter(s):
import re
s = re.sub(u'\([^()]*\)', '', s)
s = re.sub(u'!', '! ', s)
s = re.sub(u"[ ]\+", ' ', s)
s = re.sub(u' $', '', s)
s = re.sub(u'1', '1', s)
s = re.sub(u'2', '2', s)
s = re.sub(u'3', '3', s)
s = re.sub(u'4', '4', s)
s = re.sub(u'5', '5', s)
s = re.sub(u'6', '6', s)
s = re.sub(u'7', '7', s)
s = re.sub(u'8', '8', s)
s = re.sub(u'9', '9', s)
s = re.sub(u' *×[1-9]', '', s) # 繰り返しを「x2」と書くことがあるけれど、消してしまう。
n = n_maxchar
v = s.split('\n')
v = split_long_line(v, n)
i=0; s=''
for s1 in v:
i1 = my_count(s1)
if i + i1<n:
s = s+' ' + s1 if len(s) else s1
i += i1 + 1
else:
s += '\n' + s1 if len(s) else s1
i = i1
return s
def main():
s_prev = ''
while True:
try:
s_org = get_olp()
if s_org != s_prev:
s_prev = s_org
s = myfilter(s_org)
if flg_print: print u'display: %s'%s
obs_output(s)
from time import sleep
sleep(1)
except:
# OpenLPが起動していない場合など
obs_output(u'')
from time import sleep
sleep(10)
if __name__=='__main__':
main()
get_olp
で、OpenLPからテキストを取得してきます。
OpenLPが別ホストで走っている場合は、
url_base
とを書き換えましょう。
0.1秒毎に変更がないかポーリングし、変更があった場合はテキストを取得します。
ブランクかどうかの情報も取得し、ブランクならば空文字 ('') を返します。
myfilter
で表記ゆらぎを修正し、改行を整えます。改行する文字数は、
n_maxchar
で設定できます。
行頭に句読点を入れないなど、禁則処理も行います。
英語の場合は、文字によって文字幅が異なるので、OBS Studioに改行処理をやらせたほうが良いでしょう。
最後に、
obs_output
でファイルへ書き出します。
必要に応じて、出力先
fname_out
を書き換えましょう。
どのタイミングでファイルが読み込まれても良いように、一時ファイル
fname_tmp
へ書き出した後、
rename
を使ってファイルを置き換えます。
LinuxやMac OS Xではこの操作のアトミック性が保証されており、OBS Studioは書き換え途中のファイルを開くことはなく、書き換え前・書き換え後いずれかを開くはずです。
OBS Studioの設定
シーンにテキストを追加し、以下のようにファイルからの読み取りにします。
課題
OBS Studioでは、ファイルのタイムスタンプを1秒毎にチェックし、更新があれば読み出すようになっています。
テキストファイルを介するのでなくwebsocketを使ってテキストを送るスクリプトを作成しました。いずれ公開するかもしれません。