誰も見てないこちらのtwitch配信(LoL)で使ってるコードを取り敢えず置いておきます。
前提
下記コードはMITライセンスとします。
日時表示用HTML
HTMLファイルをローカルに保存し、ブラウザソースにfile:
でURLを設定して使用します。Windows用しか用意しておらずサイズ感なども固定ですが、時刻表示ではプロポーショナルフォントでも固定長の文字として表示されるよう細工しています。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <style> body { font-family: 'Impact', 'メイリオ'; width: 130px; height: 60px; margin: 0; padding: 0; color: white; } div { text-align: center; } p { margin: 0; text-shadow: 2px 2px 1px black, -2px 2px 1px black, 2px -2px 1px black, -2px -2px 1px black, 2px 0px 1px black, 0px 2px 1px black, -2px 0px 1px black, 0px -2px 1px black; } #hour-min-text { font-size: 30px; } .char { display: inline-block; width: 18px; } </style> </head> <body> <div class="content"> <p id="hour-min-text"><span id="hour" class="boxes">00</span>:<span id="minute" class="boxes">00</span>:<span id="second" class="boxes">00</span> </p> <p> <span id="date-text" >0000/00/00</span> <span id="weekday-text">日</span> </p> </div> <script> function wrap_letters(element) { element.childNodes.forEach(child=>{ if (child.nodeType === Node.TEXT_NODE) { const newE = document.createDocumentFragment(); Array.prototype.forEach.call(child.nodeValue, ch=>{ const span = document.createElement('span'); span.className = 'char'; span.textContent = ch; newE.appendChild(span); }); element.replaceChild(newE, child); } }); } const update_clock = () => { const now = new Date(); const year = ("0000" + (now.getFullYear())).slice(-4); const month = ("00" + (now.getMonth() + 1)).slice(-2); const day = ("00" + now.getDate()).slice(-2); const hour = ("00" + now.getHours()).slice(-2); const min = ("00" + now.getMinutes()).slice(-2); const sec = ("00" + now.getSeconds()).slice(-2); const week = ["日", "月", "火", "水", "木", "金", "土"][now.getDay()]; document.getElementById("hour").textContent = `${hour}`; document.getElementById("minute").textContent = `${min}`; document.getElementById("second").textContent = `${sec}`; document.getElementById("date-text").textContent = `${year}/${month}/${day}`; document.getElementById("weekday-text").textContent = `${week}`; document.querySelectorAll('.boxes').forEach(e=>wrap_letters(e)); }; setInterval(update_clock, 1000); </script> </body> </html>
録画と過去配信再生pythonスクリプト
OBSはpythonとluaスクリプトが使えます(本家リファレンス)。メニューからスクリプトでpython環境を設定し、以下のスクリプトを読み込むと設定画面が出るので、そこで録画するキャプチャソース(ゲーム画面)と、再生用のメディアソースを指定すると、配信したときに自動録画と非録画時にメディアを再生します。
import obspython as S from contextlib import contextmanager from datetime import datetime game_name = "" media_source_name = "" @contextmanager def source_auto_release(source_name): source = S.obs_get_source_by_name(source_name) try: yield source finally: S.obs_source_release(source) @contextmanager def enum_sources_auto_release(): sources = S.obs_enum_sources() try: yield sources finally: S.source_list_release(sources) def logging(message, level=S.LOG_INFO): now = datetime.now().isoformat() S.script_log(level, f'{now}: {message}') def add_logging(func): def wrapper(*args, **kwargs): unified_args_str = ','.join([str(x) for x in args] + [f'{str(t[0])}={str(t[1])}' for t in kwargs.items()]) logging(f'[entry]{func.__name__}({unified_args_str})') return func(*args, **kwargs) return wrapper def is_playing(media_source): return S.obs_source_media_get_state(media_source) in (S.OBS_MEDIA_STATE_PLAYING, S.OBS_MEDIA_STATE_OPENING, S.OBS_MEDIA_STATE_BUFFERING) def is_player_not_started(media_source): return S.obs_source_media_get_state(media_source) in (S.OBS_MEDIA_STATE_NONE, S.OBS_MEDIA_STATE_STOPPED, S.OBS_MEDIA_STATE_ENDED) @add_logging def play_pause_media_source(pause=False): with source_auto_release(media_source_name) as media_source: if is_player_not_started(media_source): S.obs_source_media_restart(media_source) else: S.obs_source_media_play_pause(media_source, pause) @add_logging def on_hooked_(data): play_pause_media_source(pause=True) S.obs_frontend_recording_start() # decoratorが効かないので def on_hooked(data): on_hooked_(data) @add_logging def on_unhooked_(data): S.obs_frontend_recording_stop() play_pause_media_source() # decoratorが効かないので def on_unhooked(data): on_unhooked_(data) @add_logging def prepare_source_callback(): with source_auto_release(game_name) as game: sh = S.obs_source_get_signal_handler(game) S.signal_handler_connect(sh, "hooked", on_hooked) S.signal_handler_connect(sh, "unhooked", on_unhooked) @add_logging def release_source_callback(): S.obs_frontend_recording_stop() with source_auto_release(game_name) as game: sh = S.obs_source_get_signal_handler(game) S.signal_handler_disconnect(sh, "hooked", on_hooked) S.signal_handler_disconnect(sh, "unhooked", on_unhooked) @add_logging def on_event(event): global pastgames_running if event == S.OBS_FRONTEND_EVENT_STREAMING_STARTING: play_pause_media_source() prepare_source_callback() elif event == S.OBS_FRONTEND_EVENT_STREAMING_STOPPING: play_pause_media_source(pause=True) release_source_callback() def extract_from_sources_in(lst): with enum_sources_auto_release() as sources: return [S.obs_source_get_name(s) for s in sources if S.obs_source_get_unversioned_id(s) in lst] @add_logging def script_update(settings): global media_source_name global game_name game_name_updated = S.obs_data_get_string(settings, "game_name") if game_name != game_name_updated: logging(f'game_name: {game_name} -> {game_name_updated}') game_name = game_name_updated media_source_name_updated = S.obs_data_get_string(settings, "media_source_name") if media_source_name != media_source_name_updated: logging(f'game_name: {media_source_name} -> {media_source_name_updated}') must_play = False with source_auto_release(media_source_name) as media_source: must_play = is_playing(media_source) media_source_name = media_source_name_updated with source_auto_release(media_source_name) as media_source: playing_now = is_playing(media_source) if must_play != playing_now: S.obs_source_media_play_pause(media_source, pause=playing_now) @add_logging def script_description(): return "配信開始時メディアを再生開始し、ゲームキャプチャ中はメディア再生を一時停止して録画し、ゲームキャプチャ終了時メディア再生を再開し、配信終了でメディア再生を一時停止する" @add_logging def script_properties(): props = S.obs_properties_create() p = S.obs_properties_add_list(props, "game_name", "ゲーム画面のキャプチャ名", S.OBS_COMBO_TYPE_LIST, S.OBS_COMBO_FORMAT_STRING) for name in extract_from_sources_in(["window_capture", "game_capture"]): S.obs_property_list_add_string(p, name, name) p = S.obs_properties_add_list(props, "media_source_name", "再生するソース名", S.OBS_COMBO_TYPE_LIST, S.OBS_COMBO_FORMAT_STRING) for name in extract_from_sources_in(["ffmpeg_source", "vlc_source"]): S.obs_property_list_add_string(p, name, name) return props @add_logging def script_load(settings): S.obs_frontend_add_event_callback(on_event)