twitch-irl-docker/build/scripts/obs_controller.py

248 lines
11 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
twitch-irl-docker
Copyright (C) 2022 gpkvt
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import sys
import time
import logging
import requests
import memcache
from lib import config
from lib import obs as obsremote
from pprint import pprint
conf = config.get_config()
# Must be AFTER import config, as config sets default logging options
logging.basicConfig(format='%(asctime)s %(module)s %(levelname)s: %(message)s')
logging.getLogger().setLevel(conf['LOG_LEVEL'])
mc = memcache.Client(['localhost:11211'], debug=0)
def pushover_send(user, token, msg):
if user and token:
logging.debug('Send message to pushover')
r = requests.post("https://api.pushover.net/1/messages.json", data={ "token": token, "user": user, "message": str(msg) })
logging.debug(r)
return r
else:
return False
def countdown(t):
while t:
if logging.getLogger().level <= 20:
mins, secs = divmod(t, 60)
timeformat = '{:02d}:{:02d}'.format(mins, secs)
sys.stdout.write(timeformat + "\r")
time.sleep(1)
t -= 1
def print_info():
logging.info('-------------------------------------------------------------------------------')
logging.info('Stream Status')
logging.info("OBS Connection : %s", str(mc.get('OBS_CONNECTED')))
logging.info("OBS Streaming : %s", str(mc.get('OBS_STREAMING_STATUS')))
logging.info("OBS Scene : %s", str(mc.get('OBS_CURRENT_SCENE')))
logging.info("Bitrate : %s", str(mc.get('RTMP_BITRATE')))
logging.info("Low threshold : %s", str(conf['LOW_BITRATE']))
logging.info("Low bitrate : %s", str(mc.get('LOW_BITRATE')))
logging.info("Publishing : %s", str(mc.get('RTMP_PUBLISHING')))
logging.info('-------------------------------------------------------------------------------')
logging.info('OBS Stats')
logging.info("CPU usage : %s", str(mc.get('OBS_CPU-USAGE')))
logging.info("Memory usage : %s", str(mc.get('OBS_MEMORY-USAGE')))
logging.info("Free disk space : %s", str(mc.get('OBS_FREE-DISK-SPACE')))
logging.info("Stream timecode : %s", str(mc.get('OBS_STREAM-TIMECODE')))
logging.info("FPS : %s", str(mc.get('OBS_FPS')))
logging.info("kbits/sec. : %s", str(mc.get('OBS_KBITS-PER-SEC')))
logging.info("bytes/sec. : %s", str(mc.get('OBS_BYTES-PER-SEC')))
logging.info("Dropped frames : %s", str(mc.get('OBS_NUM-DROPPED-FRAMES')))
logging.info("Total frames : %s", str(mc.get('OBS_NUM-TOTAL-FRAMES')))
logging.info("Output skipped frames: %s", str(mc.get('OBS_OUTPUT-SKIPPED-FRAMES')))
logging.info("Output total frames : %s", str(mc.get('OBS_OUTPUT-TOTAL-FRAMES')))
logging.info("Avg. frame time : %s", str(mc.get('OBS_AVERAGE-FRAME-TIME')))
logging.info("Render total frames : %s", str(mc.get('OBS_RENDER-TOTAL-FRAMES')))
logging.info("Render missed frames : %s", str(mc.get('OBS_RENDER-MISSED-FRAMES')))
logging.info("Total stream time : %s", str(mc.get('OBS_TOTAL-STREAM-TIME')))
logging.info("Preview only : %s", str(mc.get('OBS_PREVIEW-ONLY')))
logging.info("Recording : %s", str(mc.get('OBS_RECORDING')))
logging.info("Strain : %s", str(mc.get('OBS_STRAIN')))
logging.info("Replay Buffer : %s", str(mc.get('OBS_REPLAY-BUFFER-ACTIVE')))
logging.info('-------------------------------------------------------------------------------')
logging.info("Active VLC Sources : %s", str(mc.get('OBS_ACTIVE_VLC_SOURCES')))
logging.info("Active Filters : %s", str(mc.get('OBS_ACTIVE_FILTERS')))
logging.info("Muted Audio Sources : %s", str(mc.get('OBS_MUTED_AUDIO')))
logging.info("Volume Settings : %s", str(mc.get('OBS_VOLUME_SETTINGS')))
logging.info('-------------------------------------------------------------------------------')
return True
def main():
o = False
obs_connected = False
obs_streaming_info_resend = True
rtmp_error_info_resend = True
forced_scene_info_resend = True
low_bitrate_info_resend = True
switch_to_brb = False
pu = conf['PUSHOVER_USER_KEY']
pt = conf['PUSHOVER_APP_TOKEN']
obs_remote = obsremote.ObsRemote(conf)
current_scene = obs_remote.get_scene()
mc.set('OBS_CONNECTED', "False")
mc.set('OBS_STREAMING_STATUS', "False")
mc.set('OBS_CURRENT_SCENE', current_scene)
active_vlc_sources = ""
active_filters = ""
muted_audio = ""
while True:
while not obs_connected:
logging.debug('Trying to connect to OBS')
obs = obs_remote.connect()
if obs:
obs_connected = True
logging.debug('OBS connection established')
pushover_send(pu,pt,'OBS connection established')
mc.set('OBS_CONNECTED', "True")
else:
logging.debug('OBS is not connected, waiting...')
pushover_send(pu,pt,'OBS is not connected, next connection attempt will be in 60 seconds.')
print_info()
mc.set('OBS_CONNECTED', "False")
obs_connected = False
countdown(60)
continue
mc.set('OBS_CONNECTED', "True")
if not obs_remote.is_streaming():
logging.info('OBS is not streaming')
mc.set('OBS_STREAMING_STATUS', "False")
current_scene = obs_remote.get_scene()
mc.set('OBS_CURRENT_SCENE', current_scene)
if obs_streaming_info_resend:
pushover_send(pu,pt,'OBS is not streaming, will check again in 5 seconds. This message is only sent once.')
obs_streaming_info_resend = False
print_info()
countdown(5)
else:
obs_streaming_info_resend = True
if mc.get("rtmp_error"):
logging.error('There was an RTMP error')
if rtmp_error_info_resend:
pushover_send(pu,pt,'There was an RTMP error, will check again in 30 seconds. This message is only sent once.')
rtmp_error_info_resend = False
print_info()
countdown(30)
continue
else:
rtmp_error_info_resend = True
obs_error = obs_remote.error()
if obs_error:
logging.error('There was an OBS error (%s)', obs_error)
pushover_send(pu,pt,'There was an OBS error. Will try again in 10 seconds. Stop the stream to stop this message from being sent.')
print_info()
countdown(10)
continue
else:
mc.set('OBS_CONNECTED', "True")
o = obs_remote.get()
try:
logging.info('Fetching OBS details')
for key, value in o.items():
mc.set('OBS_' + str(key).upper(), value)
except:
logging.warning('Error submitting OBS data to memcache')
current_scene = obs_remote.get_scene()
mc.set('OBS_CURRENT_SCENE', current_scene)
current_streaming_status = obs_remote.is_streaming()
mc.set('OBS_STREAMING_STATUS', current_streaming_status)
sources_and_filter = obs_remote.get_sources_and_filter()
if sources_and_filter:
mc.set('OBS_ACTIVE_VLC_SOURCES', sources_and_filter['active_vlc_sources'])
mc.set('OBS_ACTIVE_FILTERS', sources_and_filter['active_filters'])
mc.set('OBS_ACTIVE_AUDIO', sources_and_filter['active_audio'])
mc.set('OBS_MUTED_VLC_SOURCES', sources_and_filter['muted_vlc_sources'])
mc.set('OBS_MUTED_FILTERS', sources_and_filter['muted_filters'])
mc.set('OBS_MUTED_AUDIO', sources_and_filter['muted_audio'])
mc.set('OBS_VOLUME_SETTINGS', sources_and_filter['volume_settings'])
if not mc.get('RTMP_PUBLISHING') or mc.get('RTMP_LOW_BITRATE'):
logging.debug('No RTMP client is publishing with an sufficient bitrate')
if low_bitrate_info_resend:
pushover_send(pu,pt,'No RTMP client is publishing with an sufficient bitrate. Bitrate is '+str(mc.get('RTMP_BITRATE'))+' kbit/sec. - This message is only sent once.')
low_bitrate_info_resend = False
switch_to_brb = True
else:
switch_to_brb = False
low_bitrate_info_resend = True
# Check if we're in a scene we are not allowed to switch automatically.
if current_scene.startswith('FORCED') or current_scene == conf['OBS_OFFLINE_SCENE']:
logging.info('FORCED/OFFLINE scene active, will not switch scene.')
if forced_scene_info_resend:
pushover_send(pu,pt,'FORCED/OFFLINE scene active, will not switch scene. This message is only sent once.')
forced_scene_info_resend = False
print_info()
time.sleep(1)
continue
# Check if we need to automatically switch scenes.
if switch_to_brb:
if current_scene != conf['OBS_BRB_SCENE']:
logging.debug('Switching to BRB scene')
pushover_send(pu,pt,'Switching to BRB scene. The scene will be active for at least 5 seconds.')
obs_remote.set_scene(conf['OBS_BRB_SCENE'])
countdown(10)
else:
if current_scene != conf['OBS_LIVE_SCENE']:
logging.debug('Switching from BRB to LIVE')
pushover_send(pu,pt,'Switching to LIVE scene. Bitrate is '+str(mc.get('RTMP_BITRATE'))+' kbit/sec.')
obs_remote.set_scene(conf['OBS_LIVE_SCENE'])
forced_scene_info_resend = True
if o:
if o['streaming'] == "Exit":
logging.critical('OBS was shut down!')
pushover_send(pu,pt,'OBS was shut down! Will check again in 30 seconds. This message is only sent once.')
obs_connected = False
obs_streaming_info_resend = False
rtmp_error_info_resend = False
time.sleep(30)
print_info()
time.sleep(1)
if __name__ == "__main__":
main()