#!/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 . """ 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()