import os import sys import time import calendar import logging import logging.config import shutil import random import string import json import telnetlib import math import socket from threading import Thread from subprocess import Popen, PIPE from datetime import datetime from datetime import timedelta import filecmp # For RabbitMQ from kombu.connection import BrokerConnection from kombu.messaging import Exchange, Queue, Consumer, Producer from kombu.exceptions import MessageStateError from api_clients import api_client from configobj import ConfigObj # configure logging logging.config.fileConfig("logging.cfg") # loading config file try: config = ConfigObj('/etc/airtime/pypo.cfg') LS_HOST = config['ls_host'] LS_PORT = config['ls_port'] POLL_INTERVAL = int(config['poll_interval']) except Exception, e: logger = logging.getLogger() logger.error('Error loading config file: %s', e) sys.exit() class PypoFetch(Thread): def __init__(self, q): Thread.__init__(self) logger = logging.getLogger('fetch') self.api_client = api_client.api_client_factory(config) self.set_export_source('scheduler') self.queue = q self.schedule_data = [] logger.info("PypoFetch: init complete") def init_rabbit_mq(self): logger = logging.getLogger('fetch') logger.info("Initializing RabbitMQ stuff") try: schedule_exchange = Exchange("airtime-pypo", "direct", durable=True, auto_delete=True) schedule_queue = Queue("pypo-fetch", exchange=schedule_exchange, key="foo") self.connection = BrokerConnection(config["rabbitmq_host"], config["rabbitmq_user"], config["rabbitmq_password"], "/") channel = self.connection.channel() consumer = Consumer(channel, schedule_queue) consumer.register_callback(self.handle_message) consumer.consume() except Exception, e: logger.error(e) return False return True """ Handle a message from RabbitMQ, put it into our yucky global var. Hopefully there is a better way to do this. """ def handle_message(self, body, message): try: logger = logging.getLogger('fetch') logger.info("Received event from RabbitMQ: " + message.body) m = json.loads(message.body) command = m['event_type'] logger.info("Handling command: " + command) if command == 'update_schedule': self.schedule_data = m['schedule'] self.process_schedule(self.schedule_data, "scheduler", False) elif command == 'update_stream_setting': logger.info("Updating stream setting...") self.regenerateLiquidsoapConf(m['setting']) elif command == 'cancel_current_show': logger.info("Cancel current show command received...") self.stop_current_show() except Exception, e: logger.error("Exception in handling RabbitMQ message: %s", e) finally: # ACK the message to take it off the queue try: message.ack() except MessageStateError, m: logger.error("Message ACK error: %s", m) def stop_current_show(self): logger = logging.getLogger('fetch') logger.debug('Notifying Liquidsoap to stop playback.') try: tn = telnetlib.Telnet(LS_HOST, LS_PORT) tn.write('source.skip\n') tn.write('exit\n') tn.read_all() except Exception, e: logger.debug(e) logger.debug('Could not connect to liquidsoap') def regenerateLiquidsoapConf(self, setting): logger = logging.getLogger('fetch') existing = {} # create a temp file fh = open('/etc/airtime/liquidsoap.cfg', 'r') logger.info("Reading existing config...") # read existing conf file and build dict while 1: line = fh.readline() if not line: break line = line.strip() if line.find('#') == 0: continue # if empty line if not line: continue key, value = line.split('=') key = key.strip() value = value.strip() value = value.replace('"', '') if value == "" or value == "0": value = '' existing[key] = value fh.close() # dict flag for any change in cofig change = {} # this flag is to detect diable -> disable change # in that case, we don't want to restart even if there are chnges. state_change_restart = {} #restart flag restart = False logger.info("Looking for changes...") # look for changes for s in setting: if "output_sound_device" in s[u'keyname'] or "icecast_vorbis_metadata" in s[u'keyname']: dump, stream = s[u'keyname'].split('_', 1) state_change_restart[stream] = False # This is the case where restart is required no matter what if (existing[s[u'keyname']] != s[u'value']): logger.info("'Need-to-restart' state detected for %s...", s[u'keyname']) restart = True; else: stream, dump = s[u'keyname'].split('_',1) if "_output" in s[u'keyname']: if (existing[s[u'keyname']] != s[u'value']): logger.info("'Need-to-restart' state detected for %s...", s[u'keyname']) restart = True; state_change_restart[stream] = True elif ( s[u'value'] != 'disabled'): state_change_restart[stream] = True else: state_change_restart[stream] = False else: # setting inital value if stream not in change: change[stream] = False if not (s[u'value'] == existing[s[u'keyname']]): logger.info("Keyname: %s, Curent value: %s, New Value: %s", s[u'keyname'], existing[s[u'keyname']], s[u'value']) change[stream] = True # set flag change for sound_device alway True logger.info("Change:%s, State_Change:%s...", change, state_change_restart) for k, v in state_change_restart.items(): if k == "sound_device" and v: restart = True elif v and change[k]: logger.info("'Need-to-restart' state detected for %s...", k) restart = True # rewrite if restart: fh = open('/etc/airtime/liquidsoap.cfg', 'w') logger.info("Rewriting liquidsoap.cfg...") fh.write("################################################\n") fh.write("# THIS FILE IS AUTO GENERATED. DO NOT CHANGE!! #\n") fh.write("################################################\n") for d in setting: buffer = d[u'keyname'] + " = " if(d[u'type'] == 'string'): temp = d[u'value'] if(temp == ""): temp = "" buffer += "\"" + temp + "\"" else: temp = d[u'value'] if(temp == ""): temp = "0" buffer += temp buffer += "\n" fh.write(buffer) fh.write("log_file = \"/var/log/airtime/pypo-liquidsoap/