def __init__(self, sender, lqs_socket, lqs_recsocket):
     """
     Constructor
     @type    sender: object
     @param   sender: Der Communicator Adapter - z-B. zmq
     @type    lqs_socket: string 
     @param   lqs_socket: Liquidsoap Player Socket
     @type    lqs_recsocket: string
     @param   lqs_recsocket: Liquidsoap Recorder Socket 
     """
     # Der Liquidsoap Client
     self.lqc = LiquidsoapClient(lqs_socket)
     self.lqcr = LiquidsoapClient(lqs_recsocket)
     # Felder die Liquidsoap fuer einen Track (rid) zurueckliefert
     self.knownfields = ["status", "album","time","title","artist","comment","filename","on_air","source","rid", "genre"]  
     self.lq_error = ''
     self.sender = sender
     self.is_intern = False
     self.messenger = CombaMessenger()
     self.messenger.setChannel('controller')
     self.messenger.setSection('execjob')
     self.userdb = CombaUser()
     self.job_result = ['', '', '', '','', '']
     errors_file = os.path.dirname(os.path.realpath(__file__)) + '/error/combac_error.js'
     json_data = open(errors_file)
     self.errorData = simplejson.load(json_data)
Beispiel #2
0
 def __init__(self, datefrom="", dateto=""):
     threading.Thread.__init__ (self)
     self.messenger = CombaMessenger()
     self.messenger.setChannel('helpers')
     self.messenger.setSection('getcalendar')
     self.xmlplaylist = range(0)
     self.loadConfig()
     self.messenger.setMailAddresses(self.get('frommail'), self.get('adminmail'))
     self.dateto = dateto
     self.datefrom = str(datefrom)
     self.until = ''
Beispiel #3
0
    def __init__(self, CombaClient_instance):
        """
        Constructor
        @type    CombaClient_instance: object
        @param   CombaClient_instance: Der Client für Liquidsoap
        """        
        self.client = CombaClient_instance
        
        # Messenger für Systemzustände initieren
        self.messenger = CombaMessenger()
        self.messenger.setChannel('monitor')
        self.messenger.setSection('execjob')
        
        self.playlistwatchers = []
        self.watchers = []
        
        # das pyev Loop-Object
        self.loop = pyev.default_loop()
        self.playlistwatcher_loop = pyev.Loop()
        # Die Signale, die Abbruch signalisieren
        self.stopsignals = (signal.SIGTERM, signal.SIGINT)
        
        # Das ist kein Reload
        self.initial = True
        self.config = object
        self.config_path = ""
        self.stop_time = ''
        self.start_time = ''
        self.duration = ""

        # Der Monitor wartet noch auf den Start Befehl
        self.ready = False

        self.block_combine = False
        
        errors_file = os.path.dirname(os.path.realpath(__file__)) + '/error/combam_error.js'
        json_data = open(errors_file)
        self.errorData = simplejson.load(json_data)
        self.livetime = False

        self.messenger.send('Monitor started', '0000', 'success', 'initApp' , None, 'appinternal')
Beispiel #4
0
    def __init__(self, CombaClient_instance, config):
        """
        Constructor
        @type    CombaClient_instance: object
        @param   CombaClient_instance: Der Client für Liquidsoap
        @type    config:               string
        @param   config:               Pfad zum scheduler.xml
        """
        self.client = CombaClient_instance
        self.loadConfig()
        # Messenger für Systemzustände initieren
        self.messenger = CombaMessenger()
        self.messenger.setChannel('scheduler')
        self.messenger.setSection('execjob')
        self.messenger.setMailAddresses(self.get('frommail'), self.get('adminmail'))
        self.config_path = config
        self.config = object

        # Die Signale, die Abbruch signalisieren
        self.stopsignals = (signal.SIGTERM, signal.SIGINT)

        # das pyev Loop-Object
        self.loop = pyev.default_loop()

        # Das ist kein Reload
        self.initial = True

        # Der Scheduler wartet noch auf den Start Befehl
        self.ready = False

        # DIe Config laden
        self._loadConfig()

        self.scriptdir = os.path.dirname(os.path.abspath(__file__)) + '/..'

        errors_file = os.path.dirname(os.path.realpath(__file__)) + '/error/combas_error.js'
        json_data = open(errors_file)
        self.errorData = simplejson.load(json_data)

        self.messenger.send('Scheduler started', '0000', 'success', 'initApp', None, 'appinternal')
Beispiel #5
0
class CombaScheduler(CombaBase):

    def __init__(self, CombaClient_instance, config):
        """
        Constructor
        @type    CombaClient_instance: object
        @param   CombaClient_instance: Der Client für Liquidsoap
        @type    config:               string
        @param   config:               Pfad zum scheduler.xml
        """
        self.client = CombaClient_instance
        self.loadConfig()
        # Messenger für Systemzustände initieren
        self.messenger = CombaMessenger()
        self.messenger.setChannel('scheduler')
        self.messenger.setSection('execjob')
        self.messenger.setMailAddresses(self.get('frommail'), self.get('adminmail'))
        self.config_path = config
        self.config = object

        # Die Signale, die Abbruch signalisieren
        self.stopsignals = (signal.SIGTERM, signal.SIGINT)

        # das pyev Loop-Object
        self.loop = pyev.default_loop()

        # Das ist kein Reload
        self.initial = True

        # Der Scheduler wartet noch auf den Start Befehl
        self.ready = False

        # DIe Config laden
        self._loadConfig()

        self.scriptdir = os.path.dirname(os.path.abspath(__file__)) + '/..'

        errors_file = os.path.dirname(os.path.realpath(__file__)) + '/error/combas_error.js'
        json_data = open(errors_file)
        self.errorData = simplejson.load(json_data)

        self.messenger.send('Scheduler started', '0000', 'success', 'initApp', None, 'appinternal')

    #------------------------------------------------------------------------------------------#
    def set(self, key, value):
        """
        Eine property setzen
        @type    key: string
        @param   key: Der Key
        @type    value: mixed
        @param   value: Beliebiger Wert
        """
        self.__dict__[key] = value

    #------------------------------------------------------------------------------------------#
    def reload(self):
        """
        Reload Scheduler - Config neu einlesen
        """
        self.stop()
        # Scheduler Config neu laden
        if self._loadConfig():
            self.messenger.send('Scheduler reloaded by user', '0500', 'success', 'reload', None, 'appinternal')
        self.start()

    #------------------------------------------------------------------------------------------#
    def _loadConfig(self):
        """
        Scheduler-Config importieren
        @rtype:   boolean
        @return:  True/False
        """
        # Wenn das Scheduling bereits läuft, muss der Scheduler nicht unbedingt angehalten werden         
        error_type = 'fatal' if self.initial else 'error'
        watcher_jobs = self.getJobs()
        try:
            # Die Jobs aus der Config ...
            watcher_jobs = self.getJobs()
        except:
            self.messenger.send('Config is broken', '0301', error_type, 'loadConfig', None, 'config')
            if self.initial:
                self.ready = False
            return False

            # Fehlermeldung senden, wenn keine Jobs gefunden worden sind
        if len(watcher_jobs) == 0:
            self.messenger.send('No Jobs found in Config', '0302', error_type, 'loadConfig', None, 'config')

        # Der erste Watcher ist ein Signal-Watcher, der den sauberen Abbruch ermöglicht
        self.watchers = [pyev.Signal(sig, self.loop, self.signal_cb)
                         for sig in self.stopsignals]

        # Der zweite Watcher soll das Signal zum Reload der Config ermöglicen
        sig_reload = self.loop.signal(signal.SIGUSR1, self.signal_reload)

        self.watchers.append(sig_reload)

        # Der dritte Watcher sendet alle 20 Sekunden ein Lebenszeichen
        say_alive = self.loop.timer(0, 20, self.sayAlive)

        self.watchers.append(say_alive)

        # Der vierte Watcher schaut alle 20 Sekunden nach, ob eine Vorproduktion eingespielt werden soll
        lookup_prearranged = self.loop.timer(0, 20, self.lookup_prearranged)

        self.watchers.append(lookup_prearranged)

        # Der fünfte Watcher führt initiale Jobs durch
        on_start = self.loop.timer(0, 30, self.on_start)

        self.watchers.append(on_start)

        # Nun Watcher für alle Jobs aus der Config erstellen 
        for watcher_job in watcher_jobs:
            watcher = pyev.Scheduler(self.schedule_job, self.loop, self.exec_job, watcher_job)
            # Jeder watcher wird von der Scheduler Funktion schedule_job schedult und vom Callback exec_job ausgeführt
            # watcher_job wird an watcher.data übergeben 
            # schedule_job meldet an den Loop den nächsten Zeitpunkt von watcher_job['time']
            # exec_job führt die Funktion dieser Klasse aus, die von watcher_job['job'] bezeichnet wird
            self.watchers.append(watcher)

        # Es kann losgehen        
        self.ready = True
        return True

    def getJobs(self):
        error_type = 'fatal' if self.initial else 'error'
        try:
            # Das scheduler.xml laden
            self.config = CombaSchedulerConfig(self.config_path)
        except:
            # Das scheint kein gültiges XML zu sein
            self.messenger.send('Config is broken', '0301', error_type, 'loadConfig', None, 'config')
            # Wenn das beim Start passiert können wir nix tun
            if self.initial:
                self.ready = False
            return False
        jobs = self.config.getJobs()
        
        for job in jobs:
            if job['job'] == 'start_recording' or job['job'] == 'play_playlist':
                stopjob = self._getStopJob(job)
                jobs.append(stopjob)
                
        return jobs   
            
    # -----------------------------------------------------------------------#
    def _getStopJob(self, startjob):
         job = {}
         job['job'] = 'stop_playlist' if startjob['job'] == 'play_playlist' else 'stop_recording'
         if startjob['day'] == 'all':
             job['day'] = startjob['day']
         else:
             
             if startjob['time'] < startjob['until']:
                 job['day'] = startjob['day']
             else:
                try:
                     day = int(startjob['day'])
                     stopday = 0 if  day > 5 else day+1
                     job['day'] = str(stopday)
                except:
                    job['day'] = 'all'
                
         job['time'] = startjob['until']  
         return job

    #------------------------------------------------------------------------------------------#
    def start(self):
        """
        Event Loop starten
        """
        # Alle watcher starten
        for watcher in self.watchers:
            watcher.start()

        logging.debug("{0}: started".format(self))
        try:
            self.loop.start()
        except:
            self.messenger.send("Loop did'nt start", '0302', 'fatal', 'appstart', None, 'appinternal')
        else:
            self.messenger.send("Scheduler started", '0100', 'success', 'appstart', None, 'appinternal')

    #------------------------------------------------------------------------------------------#
    def stop(self):
        """
        Event Loop stoppen
        """
        self.loop.stop(pyev.EVBREAK_ALL)
        # alle watchers stoppen und entfernen
        while self.watchers:
            self.watchers.pop().stop()
        self.messenger.send("Loop stopped", '0400', 'success', 'appstart', None, 'appinternal')

    #------------------------------------------------------------------------------------------#
    def sayAlive(self, watcher, revents):
        """
        Alle 20 Sekunden ein Lebenssignal senden
        @type  watcher:  object
        @param watcher:  Das watcher Objekt
        @type  revents: object
        @param revents: Event Callbacks    
        """
        self.messenger.sayAlive()

    #------------------------------------------------------------------------------------------#
    def signal_cb(self, loop, revents):
        """
        Signalverarbeitung bei Abbruch
        @type  loop: object
        @param loop: Das py_ev loop Objekt
        @type  revents: object
        @param revents: Event Callbacks
        """
        self.messenger.send("Received stop signal", '1100', 'success', 'appstop', None, 'appinternal')
        self.stop()

    #------------------------------------------------------------------------------------------#
    def signal_reload(self, loop, revents):
        """
        Lädt Scheduling-Konfiguration neu bei Signal SIGUSR1
        @type  loop: object
        @param loop: Das py_ev loop Objekt
        @type  revents: object
        @param revents: Event Callbacks        
        """
        self.messenger.send("Comba Scheduler gracefull restarted", '1200', 'success', 'appreload', None, 'appinternal')
        self.reload()

    #------------------------------------------------------------------------------------------#
    def schedule_job(self, watcher, now):
        """
        Callback zum Scheduling eines Jobs
        @type  watcher:  object
        @param watcher:  Das watcher Objekt
        @type  now:      float 
        @param now:      Aktuelle Zeit in Sekunden        
        @rtype:          float
        @return:         Die Zeit zu der der Job ausgeführt werden soll in Sekunden
        """
        # nächstes Ereignis dieses Watchers aus den watcher data
        data = watcher.data.copy()
        next_schedule = data['time']

        # Minuten und Stunden
        (next_hour, next_minute) = next_schedule.split(':')

        # Zum Vergleich die aktuelle und die auszuführende Unhrzeit in Integer wandeln
        today_time = int(datetime.datetime.now().strftime('%H%M'))
        next_time = int(next_hour + next_minute)

        # Wenn der Job erst morgen ausgeführt werden soll ist day_offset 1 
        day_offset = 1 if (today_time >= next_time) else 0

        # Ist ein Tag angegeben
        if data.has_key('day'):
            try:
                #Montag ist 0 
                dayofweek = int(data['day'])
                delta = relativedelta(hour=int(next_hour), minute=int(next_minute), second=0, microsecond=0,
                                      weekday=dayofweek)
            except:
                #Fallback -  day ist vermutlich ein String
                delta = relativedelta(hour=int(next_hour), minute=int(next_minute), second=0, microsecond=0)
        else:
            delta = relativedelta(hour=int(next_hour), minute=int(next_minute), second=0, microsecond=0)

        # Ermittle das Datumsobjekt 
        schedule_result = datetime.datetime.now() + timedelta(day_offset) + delta

        # In Sekunden umrechnen
        result = time.mktime(schedule_result.timetuple())

        schedule_time_human = datetime.datetime.fromtimestamp(int(result)).strftime('%Y-%m-%d %H:%M:%S')
        time_now = datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S')
        date_human = datetime.datetime.fromtimestamp(int(result)).strftime('%Y-%m-%d')
        time_human = datetime.datetime.fromtimestamp(int(result)).strftime('%H:%M')
        # Events feuern, zum stoppen und starten einer Playlist
        # TODO: Diese events müssen bei einem Reset gelöscht werden       
        # Es sollte sicher sein, einfach alle keys mit  playerevent_*_playliststart unc playerevent_*_playliststop zu löschen
        if data.has_key('job'):
            if data['job'] == 'play_playlist':
                event = {'job': 'play_playlist', 'date': date_human, 'time': time_human}
                self.messenger.queueAddEvent('playliststart', str(schedule_time_human).replace(' ', 'T'), event,
                                             'player')
            if data['job'] == 'stop_playlist':
                event = {'job': 'stop_playlist', 'date': date_human, 'time': time_human}
                self.messenger.queueAddEvent('playliststop', str(schedule_time_human).replace(' ', 'T'), event,
                                             'player')

        data['scheduled_at'] = time_now
        data['scheduled_for'] = schedule_time_human

        self.info('schedule_job', data, '00', simplejson.dumps(data), 'schedulejob')

        # das nächste mal starten wir diesen Job in result Sekunden 
        return result

    #------------------------------------------------------------------------------------------#
    def exec_job(self, watcher, revents):
        """
        Callback, um einen Job auszuführen
        @type  watcher:  object
        @param watcher:  Das watcher Objekt
        @type  revents:  object
        @param revents:  Event Callbacks     
        """
        data = watcher.data.copy()

        # Welcher Job ausgeführt werden soll wird in watcher.data vermerkt
        job = data['job']

        # Job ausführen
        try:
            exec "a=self." + job + "(data)"
        except Exception, e:
            data['exec'] = 'exec"a=self.' + job + '(' + simplejson.dumps(data) + ')"'
            data['Exception'] = str(e)
            self.fatal('exec_job', data, '01', simplejson.dumps(data))
            watcher.stop()  #stop the watcher
        else:
Beispiel #6
0
class CombaCalendarService(threading.Thread, CombaBase):
    """
    Holt Playlist-Daten und schreibt sie in die database
    """
    def __init__(self, datefrom="", dateto=""):
        threading.Thread.__init__ (self)
        self.messenger = CombaMessenger()
        self.messenger.setChannel('helpers')
        self.messenger.setSection('getcalendar')
        self.xmlplaylist = range(0)
        self.loadConfig()
        self.messenger.setMailAddresses(self.get('frommail'), self.get('adminmail'))
        self.dateto = dateto
        self.datefrom = str(datefrom)
        self.until = ''

    #------------------------------------------------------------------------------------------#
    def setDateFrom(self, date):
        self.datefrom = str(date).replace(" ", "T")

    #------------------------------------------------------------------------------------------#
    def setDateTo(self, date):
        self.dateto = str(date).replace(" ", "T")

    #------------------------------------------------------------------------------------------#
    def setUntilTime(self, timestring):
        self.until = timestring

    #------------------------------------------------------------------------------------------#
    def setSecondsPerTrack(self, seconds):
        self.secondspertrack = int(seconds)

    #------------------------------------------------------------------------------------------#
    def setCalendarUrl(self, url):
        self.calendarurl = url

    #------------------------------------------------------------------------------------------#
    def setAudioPath(self, path):
        self.audiobase = path

    #------------------------------------------------------------------------------------------#
    def setAudioPath(self, path):
        self.audiobase = path

    #------------------------------------------------------------------------------------------#
    def setPlaylistStore(self, path):
        self.playlistdir = path

    #------------------------------------------------------------------------------------------#
    def getUri(self):
        if not self.playlistdir:
            return False
        if not self.datefrom:
            return False
        if not self._calcDateTo():
            return
        hostname = self.get('servername');
        port = self.get('serviceport');
        date_from = self.datefrom[0:16] + ':00';
        date_to = self.dateto[0:16] + ':00';
        uri = 'http://' + hostname  + ':' + port + '/playlist/' + date_from + '/' + date_to

        return uri

    #------------------------------------------------------------------------------------------#
    def run(self):
        """
        Kalender-Daten abholen und in Playlist schreiben
        """
        filestotal = 0
        if not self._calcDateTo():
            return

        self._setUrl()
        self._fetchData()

        # secondspertrack  may be a string from config
        self.secondspertrack = int(self.secondspertrack)
        for show in self.data['shows']:
            #fix nonexisting origdatetime
            if not show.has_key('rerun'):
                continue
            if not show.has_key('end'):
                continue
            if not show.has_key('start'):
                continue

            if str(show['rerun']).lower() == 'true':
                try:
                    show['origdate'] = show['replay_of_datetime']
                except KeyError:
                    continue
            else:
                show['origdate'] = show['start']
                #TODO: message
                #mailnotice("Am " + datefrom[0:10] +" wurde eine Luecke im Kalender festgestellt")


            event = BroadcastEvent.objects(identifier=show['identifier']).first()

            if not event:
                event = BroadcastEvent()

            # calc duration
            duration = self._calcDuration(show['start'], show['end'])
            show['duration'] = datetime.timedelta(seconds=duration).__str__()
            for k in event:
                if show.has_key(k):
                    event[k] = show[k]

            event.modified = datetime.datetime.now()
            event.modified_by = 'calendarservice'

            event.save()
            broadcast_event = event.to_dbref()

            trackstart = datetime.datetime.strptime(show['start'].replace(' ', 'T'), "%Y-%m-%dT%H:%M:%S")
            trackrecorded = datetime.datetime.strptime(show['origdate'].replace(' ', 'T'), "%Y-%m-%dT%H:%M:%S")
            # split in parts
            parts = duration / self.secondspertrack

            for i in range(0, parts):
                filestotal += 1

                # Zu dieser Zeit muss die Aufnahme beendet werden
                trackDumpend = trackrecorded + datetime.timedelta(seconds=self.secondspertrack)
                trackPlayend = trackstart + datetime.timedelta(seconds=self.secondspertrack)

                identifier = show['identifier'] + ':' + str(i)
                track = BroadcastEventTrack.objects(identifier=identifier).first()
                if not track:
                    track = BroadcastEventTrack()

                File = str(self.audiobase + '/').replace('//','/') + trackrecorded.strftime('%Y-%m-%d') + '/' + trackrecorded.strftime('%Y-%m-%d-%H-%M') + '.wav'
                track.identifier = identifier
                track.title = show['title']
                track.ordering = i
                track.broadcast_event = broadcast_event
                track.location = 'file://' + File
                track.length = self.secondspertrack
                track.start = trackstart.strftime('%Y-%m-%d %H:%M:%S')
                track.end = trackPlayend.strftime('%Y-%m-%d %H:%M:%S')
                track.record_at = trackrecorded.strftime('%Y-%m-%d %H:%M:%S')

                track.save()

                # Events festhalten
                event = {'job': 'dump', 'location': File, 'length': track.length}
                self.messenger.queueAddEvent('dumpstart', track.record_at, event, 'recorder')
                event = {'job': 'dump', 'location': File, 'length': track.length}
                self.messenger.queueAddEvent('dumpend', trackDumpend, event, 'recorder')

                trackstart = trackstart + datetime.timedelta(seconds=self.secondspertrack)
                trackrecorded = trackrecorded + datetime.timedelta(seconds=self.secondspertrack)

    #------------------------------------------------------------------------------------------#
    def _calcDateTo(self):
        if self.dateto:
            return True
        if not self.until:
            return False
        if not self.datefrom:
            return False
        date_start = datetime.datetime.strptime(self.datefrom.replace('T',' '), "%Y-%m-%d %H:%M")
        time_start = date_start.strftime('%H:%M')
        day_offset = 1 if (time_start > self.until) else 0
        end_date = date_start + datetime.timedelta(day_offset)
        self.dateto = end_date.strftime('%F') + 'T' + self.until
        return True

    #------------------------------------------------------------------------------------------#
    def _calcDuration(self, start, end):
        """
        Berechnet Zeit in Sekunden aus Differenz zwischen Start und Enddatum
        @type start: datetime
        @param start: Startzeit
        @type end: datetime
        @param end: Endzeit
        @rtype:       int
        @return:      Zeit in Sekunden
        """
        sec1 = int(datetime.datetime.strptime(start[0:16].replace(" ","T"),"%Y-%m-%dT%H:%M").strftime("%s"));
        sec2 = int(datetime.datetime.strptime(end[0:16].replace(" ","T"),"%Y-%m-%dT%H:%M").strftime("%s"));
        return (sec2 - sec1);


    #------------------------------------------------------------------------------------------#
    def _fetchData(self):

        # Now open Calendar Url
        try:
            response = urllib2.urlopen(self.dataURL)

        except IOError, e:
            self.messenger.send("Could not connect to service " + self.dataURL, '1101', 'error', 'fetchCalenderData',
                                self._getErrorData(), 'getcalendar')
            #mailnotice("Verbindung zu Zappa konnte nicht hergestellt werden")
            #TODO: so gehts nicht
            sys.exit()
        else:
Beispiel #7
0
class CombaMonitor(CombaBase):
    # Constructor
    def __init__(self, CombaClient_instance):
        """
        Constructor
        @type    CombaClient_instance: object
        @param   CombaClient_instance: Der Client für Liquidsoap
        """        
        self.client = CombaClient_instance
        
        # Messenger für Systemzustände initieren
        self.messenger = CombaMessenger()
        self.messenger.setChannel('monitor')
        self.messenger.setSection('execjob')
        
        self.playlistwatchers = []
        self.watchers = []
        
        # das pyev Loop-Object
        self.loop = pyev.default_loop()
        self.playlistwatcher_loop = pyev.Loop()
        # Die Signale, die Abbruch signalisieren
        self.stopsignals = (signal.SIGTERM, signal.SIGINT)
        
        # Das ist kein Reload
        self.initial = True
        self.config = object
        self.config_path = ""
        self.stop_time = ''
        self.start_time = ''
        self.duration = ""

        # Der Monitor wartet noch auf den Start Befehl
        self.ready = False

        self.block_combine = False
        
        errors_file = os.path.dirname(os.path.realpath(__file__)) + '/error/combam_error.js'
        json_data = open(errors_file)
        self.errorData = simplejson.load(json_data)
        self.livetime = False

        self.messenger.send('Monitor started', '0000', 'success', 'initApp' , None, 'appinternal')
        
    #------------------------------------------------------------------------------------------#
    def _loadConfig(self):
        """
        Scheduler-Config importieren
        @rtype:   boolean
        @return:  True/False
        """        
        # Wenn das Scheduling bereits läuft, muss der Scheduler nicht unbedingt angehalten werden         
        error_type = 'fatal' if self.initial else 'error'
        try:
            # Das scheduler.xml laden
            watcher_jobs = self.config.getJobs()
        except:

            # Das scheint kein gültiges XML zu sein
            self.messenger.send('Config is broken', '0301', error_type, 'loadConfig' , None, 'config')
            # Wenn das beim Start passiert können wir nix tun
            if self.initial: 
                self.ready = False                                
            return False

        # Fehlermeldung senden, wenn keine Jobs gefunden worden sind
        if len(watcher_jobs) == 0:
            self.messenger.send('No Jobs found in Config', '0302', error_type, 'loadConfig' , None, 'config')
                 
        # Anhand der Jobs weitere Daten ermitteln, die für interne Zwecke benötigt werden
        self._get_jobs_infos(watcher_jobs)
        # Es kann losgehen        
        self.ready = True
        return True

    #------------------------------------------------------------------------------------------#
    def _get_jobs_infos(self, jobs):
        """
        Ermittelt aus der Start- und Stopzeit der Playlist Dauer der Automatisierung und die Startzeit
        @type    jobs: list
        @param   jobs: Liste mit den Jobs
        """

        self.livetime = True
        now = datetime.datetime.now()
        # Jobs nach Start und Stop-Zeit der Playlist durchsuchen

        for job in jobs:

            if job['job'] ==  'play_playlist':
                if self.config.in_timeperiod(now,job):
                    self.livetime   = False
                    self.start_time = job['time']
                    self.stop_time  = job['until']
                    self.duration   = int(job['duration'])

            elif job['job'] ==  'start_recording' and self.config.in_timeperiod(now,job):
                self.start_time = job['time']
                self.stop_time  =  job['until']
                self.duration = int(job['duration'])

    #------------------------------------------------------------------------------------------#
    def set(self, key, value):
        """
        Eine property setzen
        @type    key: string
        @param   key: Der Key
        @type    value: mixed
        @param   value: Beliebiger Wert
        """
        self.__dict__[key] = value        

    #------------------------------------------------------------------------------------------#
    def start(self):      
        """
        Monitor Loop starten
        """
        self.config = CombaSchedulerConfig(self.config_path)
        #TODO: Unterscheide bei den Funktionsnamen zwischen scheduler.xml und comba.ini
        ## die comba.ini laden
        self.loadConfig()
        self.messenger.setMailAddresses(self.get('frommail'), self.get('adminmail'))
        ## die scheduler.xml laden
        self._loadConfig()

        # Der erste Watcher ist ein Signal-Watcher, der den sauberen Abbruch ermöglicht
        self.watchers = [pyev.Signal(sig, self.loop, self.signal_cb)
                         for sig in self.stopsignals]
        
        self.watchers.append(self.loop.timer(0, 20, self.checkComponents))
        
        self.watchers.append(self.loop.timer(0, 60, self.checkPlaylistEvent))        
        
        # Der dritte Watcher sendet alle 20 Sekunden ein Lebenszeichen
        say_alive = self.loop.timer(0, 20, self.sayAlive) 

        #self.audiowatcher = self.loop.timer(0, 0,self.combine_audiofiles, self.audiowatcher_data)
        self.watchers.append(say_alive)
        
        # Alle watcher starten
        for watcher in self.watchers:
            watcher.start()        
        
        logging.debug("{0}: started".format(self))             
        try:
            self.loop.start()
        except:
            self.messenger.send("Loop did'nt start", '0101', 'fatal', 'appstart' , None, 'appinternal')
        else:
            self.messenger.send("Monitor started", '0100', 'success', 'appstart' , None, 'appinternal')    
    
    #------------------------------------------------------------------------------------------#
    def stop(self):
        """
        Event Loop stoppen
        """
        self.loop.stop(pyev.EVBREAK_ALL)
        # alle watchers stoppen und entfernen
        while self.watchers:
            self.watchers.pop().stop()
        self.messenger.send("Loop stopped", '0400', 'success', 'appstart' , None, 'appinternal')        

    #------------------------------------------------------------------------------------------#
    def sayAlive(self, watcher, revents):
        """
        Alle 20 Sekunden ein Lebenssignal senden
        @type  watcher:  object
        @param watcher:  Das watcher Objekt
        @type  revents: object
        @param revents: Event Callbacks    
        """
        self.messenger.sayAlive()

    #------------------------------------------------------------------------------------------#
    def signal_cb(self, loop, revents):
        """
        Signalverarbeitung bei Abbruch
        @type  loop: object
        @param loop: Das py_ev loop Objekt
        @type  revents: object
        @param revents: Event Callbacks
        """
        self.messenger.send("Received stop signal", '1100', 'success', 'appstop' , None, 'appinternal')           
        self.stop()

    #------------------------------------------------------------------------------------------#
    def checkComponents(self, watcher, revents):
        """
        Alle 20 Sekunden Checken, ob Komponenten noch laufen
        @type  watcher:  object
        @param watcher:  Das watcher Objekt
        @type  revents: object
        @param revents: Event Callbacks    
        """

        if not self.messenger.getAliveState('scheduler'):            
            self.messenger.send("Scheduler is down... try to start scheduler", '0201', 'error', 'checkComponents' , None, 'appinternal')
            self._restartScheduler()

        if not self.messenger.getAliveState('controller'):            
            self.messenger.send("Controller is down... try to start controller", '0202', 'error', 'checkComponents' , None, 'appinternal')
            self._restartController()
        
        if not self.messenger.getAliveState('record') or not self.messenger.getAliveState('altrecord') or not self.messenger.getAliveState('playd'):
            self.messenger.send("Liquidsoap components are down... try to start soundengine components", '0202', 'error', 'checkComponents' , None, 'appinternal')
            self._restartLiquidsoap()
            time.sleep(10);

            jobs = self.config.getJobs()
            self._get_jobs_infos(jobs)
            if self.livetime:
                for i in '123':                    
                    if self.messenger.getAliveState('record'):
                        self.messenger.send("Start recorder", '0204', 'info', 'checkComponents' , None, 'appinternal')
                        start_new_thread(self._restartRecord,())                                                                    
                        break;
                    time.sleep(20);
            else:
                for i in '123':                    
                    if self.messenger.getAliveState('playd'):

                        self.messenger.send("Start playlist", '0205', 'info', 'checkComponents' , None, 'appinternal')
                        start_new_thread(self._reloadPlaylist,())                        
                        break;
                    time.sleep(20);

        if not self.messenger.getAliveState('archive'):
            self.messenger.send("Archive is down... try to start archive", '0201', 'error', 'checkComponents' , None, 'appinternal')
            self._restartArchive()


    #------------------------------------------------------------------------------------------#
    def checktrack(self, watcher, revents):
        """
        Einen Track checken und ggf. den Player anhalten oder skippen
        @type  watcher:  object
        @param watcher:  Das watcher Objekt
        @type  revents: object
        @param revents: Event Callbacks
        """
        
        def playlist_stop(wait):
            """
            Interne Routine - stoppt den Player
            @type wait: string
            @param wait: Anz. Sekunden nach der der Player wieder gestartet wird
            """
            self.client.playlist_stop()
            time.sleep(int(wait))
            self.client.playlist_play()
        
        def playlist_skip(wait):
            """
            Interne Routine - skippt den Player
            @type wait: string
            @param wait: Anz. Sekunden nach der der Player geskippt wird
            """            
            time.sleep(int(wait))
            self.client.playlist_skip()            
        # Watcher stoppen
        watcher.stop()
        stopforDuration = 0
        skipafterDuration = 0         
        
        if not watcher.data.has_key('location'):
            return False    
        
        # Dauer des Tracks lt. Playlist
        duration = int(watcher.data['length'])  
        
        # Dateipfad
        fname = watcher.data['location'].replace('file://', '')
        
        # Wenn die Datei nicht existiert, muss der Player für die Dauer des Tracks angehalten werden
        if not os.path.isfile(fname):
            stopforDuration = str(duration)        
            start_new_thread(playlist_stop,(stopforDuration,))

        else:
            # Dauer der WAV-Datei ermitteln
            with contextlib.closing(wave.open(fname,'r')) as f:
                frames = f.getnframes()
                rate = f.getframerate()
                fduration = int(math.ceil(frames / float(rate)))
                
                # Ist die Dauer der WAV-Datei kleiner als die angegebene Dauer des Tracks,
                # muss  die Playlist um die Differenz angehalten werden  
                if fduration < duration:
                    stopforDuration = str(duration - fduration)
                    start_new_thread(playlist_stop,(stopforDuration,))

                # Ist die Dauer der WAV-Datei größer als die angegebene Dauer des Tracks,
                # muss der laufende Track nach der Diffenz geskippt
                elif fduration > duration:
                    skipafterDuration = str(fduration - duration)
                    start_new_thread(playlist_skip,(skipafterDuration,))

    #------------------------------------------------------------------------------------------#
    def checkPlaylistEvent(self, watcher, revents):
        """
        Prüft ob der LoaPlaylist event gefeurt wurde und reagiert ggf.
        """            
        eventFired = self.messenger.getEvent('loadplaylist','player')
        if eventFired:
            eventQueue = self.messenger.getEventQueue('playtrack', 'player')
            
               
            if not eventQueue or not isinstance(eventQueue, list):
                self.messenger.send('No event queue present', '1301', 'warning', 'checkPlaylistEvent' , None, 'events')

            else:
                
                # stop all playlist watchers
                for watcher in self.playlistwatchers:
                    watcher.stop()
                    self.playlistwatchers = []
                cnt = 0
                self.playlistwatcher_loop.stop()
                self.playlistwatcher_loop = pyev.Loop()
    
                for index, item in enumerate(eventQueue):
                    cnt = cnt + 10
                    if not item.has_key('date') or not item.has_key('time'):
                        # funny
                        continue
                    
                    # Sekunden berechnen nach der Timer gestartet wird
                    track_time = int(datetime.datetime.strptime(item['date'][0:10] + 'T' + item['time'],"%Y-%m-%dT%H:%M").strftime("%s")) - 1
                    self.playlistwatchers.append(self.playlistwatcher_loop.periodic(track_time, 0.0, self.checktrack, item))

                for watcher in self.playlistwatchers:
                    watcher.start()    
                       
    #------------------------------------------------------------------------------------------#
    def combine_audiofiles(self, watcher, revents):
        """
        Kombiniert Dateien, die in einem best. Zeitintervall entstanden sind
        @type watcher:  object
        @param watcher: der watcher
        @type revents:    object
        @param revents:  revents - nicht verwendet
        """
        self.block_combine = False
        self.messenger.send('Stop watcher', '0501', 'info', 'combine_audiofiles' , None, 'appinternal')
        
        from_time = int(watcher.data['from_time'])
        to_time = int(watcher.data['to_time'])
        
        watcher.stop()
        watcher.loop.stop()
        # Den Ordner  bestimmen, in dem die Dateien liegen
        cur_folder = self.record_dir + '/' + datetime.datetime.fromtimestamp(from_time).strftime("%Y-%m-%d") + "/"
        
        uid = os.stat(cur_folder).st_uid
        gid = os.stat(cur_folder).st_gid
        # Name der Audiodatei, die angelegt werden muss
        out_file = datetime.datetime.fromtimestamp(from_time).strftime("%Y-%m-%d-%H-%M") + ".wav"

        # Alle WAV-Dateien im Ordner
        files = glob.glob(cur_folder + "*.wav")
        
        combine = []
        if len(files) > 0:
            for file in files:            
                t = int(os.path.getmtime(file))            
                # Liegt die Mtime im definierten Intervall?
                if t > from_time and t <= to_time:
                    combine.append(file)

            if len(combine) > 0:

                audiotools = CombaAudiotools()
                audiotools.combine_audiofiles(combine, cur_folder, out_file, nice=19)
                os.chown(out_file, uid, gid);
                self.messenger.send("Combined  to file " + out_file, '0502', 'info', 'combine_audiofiles' , None, 'appinternal')

    #------------------------------------------------------------------------------------------#
    def _load_playlist(self):
        """
        Playlist laden und von dem Track und Zeitpunkt an starten, der aktuell laufen sollte
        """

        self.messenger.send("Try to load playlist", '0501', 'info', 'loadPlaylist' , None, 'appinternal')

        # Die Playlist holen
        store = CombaCalendarService()

        # start_date ist das Datum, an dem die Playlist beginnen soll - abrunden auf den Beginn der denierten Länge für einen Track
        start_date  = datetime.datetime.now() - datetime.timedelta(seconds=int(datetime.datetime.now().strftime('%s')) % int(self.secondspertrack))

        store.setDateFrom(start_date.strftime("%Y-%m-%dT%H:%M"))
        store.setUntilTime(self.stop_time)

        store.start()
        uri = store.getUri()

        # wait until childs thread returns
        store.join()

        result = self.client.playlist_load(uri)
            
        if not self._get_data(result):
            self.messenger.send('Could not get Data from controller', '0504', 'fatal', 'loadPlaylist' , None, 'appinternal')
        else:
            result = self.client.playlist_play()
            if not self._get_data(result):
                self.messenger.send('Could not get Data from controller', '0505', 'fatal', 'loadPlaylist' , None, 'appinternal')

            else:

                time.sleep(0.2)
                unix_time_now = int(datetime.datetime.now().strftime('%s'))
                # berechnen die Sekunden die wir noch weiterspulen müssen
                seek_time = unix_time_now - int(start_date.strftime('%s'))
                # ... und seek
                self.client.playlist_seek(str(seek_time))
                       
    #------------------------------------------------------------------------------------------#
    def _restartScheduler(self):
        """
        Scheduler neu starten
        """
        os.system("service combascheduler restart")

    #------------------------------------------------------------------------------------------#
    def _restartArchive(self):
        """
        Restart archive controller
        """
        os.system("service combaarchive restart")

    #------------------------------------------------------------------------------------------#
    def _restartController(self):
        """
        Controller neu starten
        """
        os.system("service comba restart")

    #------------------------------------------------------------------------------------------#
    def _restartLiquidsoap(self):
        """
        Player und recorder neu starten
        """
        os.system("service comba_liq restart")

    #------------------------------------------------------------------------------------------#
    def _restartRecord(self):
        """
        Recorder neu starten
        Da die Aufnahme vermutlich unterbrochen wurde, werden Audiodateien aus dem vorgesehenen Zeitraum zusammengefügt
        """
        result = self.client.recorder_start()
        if self.block_combine:
            return
        
        self.block_combine = True
        now = int(datetime.datetime.now().strftime("%s"))

        mod = now % int(self.secondspertrack)
        from_time = (now - mod)
        to_time = from_time + int(self.secondspertrack)
        # combine_audiofiles wird 1 Sekunde nach Ende der Aufnahme ausgeführt 
        time_start = float((to_time - now) +1) 
        
        data = {}
        data['from_time'] = from_time
        data['to_time'] = to_time 
        loop = pyev.Loop()
        #self.audiowatcher.set(0.0, time_start)
        
        watcher = loop.timer(time_start, 0, self.combine_audiofiles, data)
        #self.audiowatcher.set(10, 0.0)
        self.messenger.send("Audiofiles may be combined in " + str(watcher.remaining) + " seconds", '0601', 'info', 'restartRecord' , None, 'appinternal')
        
        watcher.start()
        loop.start()
        
    #------------------------------------------------------------------------------------------#
    def _reloadPlaylist(self):
        """
        Playliste neu laden und starten
        """
        self._load_playlist()

    #------------------------------------------------------------------------------------------#
    def _adminMessage(self,message):
        pass

    #------------------------------------------------------------------------------------------#
    def _get_data(self, result):
        """                
        @type     result: dict
        @param    result: Ein dict objekt
        """        
        if type(result) == type(str()):
            try:
                result = simplejson.loads(result)
            except:
                return False
        try:
            if result['success'] == 'success':
                if result.has_key('value') and result['value']:
                    return result['value']
                else:

                    return True
            else:
                return False
        except:

            return False   
class CombaController(object):
    
    # Constructor
    def __init__(self, sender, lqs_socket, lqs_recsocket):
        """
        Constructor
        @type    sender: object
        @param   sender: Der Communicator Adapter - z-B. zmq
        @type    lqs_socket: string 
        @param   lqs_socket: Liquidsoap Player Socket
        @type    lqs_recsocket: string
        @param   lqs_recsocket: Liquidsoap Recorder Socket 
        """
        # Der Liquidsoap Client
        self.lqc = LiquidsoapClient(lqs_socket)
        self.lqcr = LiquidsoapClient(lqs_recsocket)
        # Felder die Liquidsoap fuer einen Track (rid) zurueckliefert
        self.knownfields = ["status", "album","time","title","artist","comment","filename","on_air","source","rid", "genre"]  
        self.lq_error = ''
        self.sender = sender
        self.is_intern = False
        self.messenger = CombaMessenger()
        self.messenger.setChannel('controller')
        self.messenger.setSection('execjob')
        self.userdb = CombaUser()
        self.job_result = ['', '', '', '','', '']
        errors_file = os.path.dirname(os.path.realpath(__file__)) + '/error/combac_error.js'
        json_data = open(errors_file)
        self.errorData = simplejson.load(json_data)

    #------------------------------------------------------------------------------------------#
    def message(self, message, log=False, warning=False):
        """
        Daten an einen Client senden oder bei einer internen Aktion loggen
        @type     message: string
        @param    message: String, der gesendet wird
        @type     log: boolean
        @param    log: Wenn falsch, wird die message an den Client gesendet
        @type     warning: boolean
        @param    warning: Wenn falsch, ist der logging typ info, andernfalls warning
        @rtype:   string/None
        @return:  Die Message, falls log false ist
        """
        if not self.is_intern:
            self.sender.send(message)
        else:
            return message
        if log: 
            if warning:
                    logging.warning(message) 
            else:     
                logging.info(message) 

    #------------------------------------------------------------------------------------------#
    def allData(self):
        """
        Gibt Metadaten aller Kanäle als JSON-String an den Client zurück 
        @rtype:   string/None
        @return:  Die Antwort des Liquidsoap-Servers

        """        
        channels = self.sendLqcCommand(self.lqc, 'listChannels')
        
        if not isinstance(channels, list):
            self.warning('01')
            self.notifyClient()
            return 
        
        data = {}
        pdata = {}
                      
        try:
            self.is_intern = True  
            playlist_data = simplejson.loads(self.playlist_data(True))
            self.is_intern = False
        except:
            self.warning('01')            
            self.notifyClient()
            return
                    
        # Status des Playlistkanals abfragen
        status = self.sendLqcCommand(self.lqc, 'status', 'mixer', '0')
        
        states = status.split(' ')
        state_data = {}
        
        # Die Stati in python dicts einlesen
        for state in states:
            item = state.split('=')
            try:
                state_data[item[0]] = item[1]
            except:
                self.warning('01')
                self.notifyClient()
                return
        
        remaining = self.sendLqcCommand(self.lqc, 'playlist_remaining')
        state_data['remaining'] = remaining
        # Die Metadaten der Playlist
        pdata['state'] = state_data
        pdata['tracks'] = playlist_data        
        data['playlist'] = pdata
        
        # Servermeldungen abschalten
        self.is_intern = True                
        # die channel queues einlesen
        
        for channel in channels:
            data[channel] = self.channel_queue(channel, True)        
        # Servermeldungen einschalten
        self.is_intern = False
        self.success('00', data)
        self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def ping(self):
        """
        dem Client antworten
        """
        return self.message('OK')

    #------------------------------------------------------------------------------------------#
    def channel_insert(self, channel, uri, pos):
        """
        Track in einen Channel einfuegen 
        @type     channel: string
        @param    channel: Kanal
        @type     uri:     string
        @param    uri:     Uri - z.B. file:///my/audio/mp3 
        @type     pos:     int
        @param    pos:     Die Position an der eingefügt werden soll        
        @rtype:   string/None
        @return:  Die Antwort des Liquidsoap-Servers                
        """
        message = self.sendLqcCommand(self.lqc, 'insert', uri, pos, channel)
        message = message.strip()
        
        try:
            if int(message) > -1:                
                self.success()
                return self.message(message)        
        except:
            self.warning('01')
            self.notifyClient()                    

    #------------------------------------------------------------------------------------------#
    def channel_move(self, channel, fromPos, toPos):        
        """
        Channel-Eintrag von Position fromPos nach Position toPos verschieben 
        @type     channel: string
        @param    channel: Kanal
        @type     fromPos: int
        @param    fromPos: die Position des Eintrags, der verschoben wird 
        @type     toPos:   int
        @param    toPos:   Zielposition             
        @rtype:   string
        @return:  Die Antwort des Liquidsoap-Servers           
        """

        message = self.sendLqcCommand(self.lqc, 'get_queue', channel,'secondary_queue')

        rids = message.strip().split(' ')
        
        try:
            rid = rids[int(fromPos)-1]
        except:
            self.warning('01')
            self.notifyClient()
            return
        try:
            target = rids[int(toPos)-1]
        except:
            self.warning('01') 
            self.notifyClient()  
            return
            
        if rids[int(fromPos)-1] == rids[int(toPos)-1]:
            self.warning('02')
            self.notifyClient()
            return
        
        message = self.sendLqcCommand(self.lqc, 'move', rid, str(int(toPos)-1), channel)        
        message = message.strip()

        if message.strip().find('OK') > -1:
                self.success()                                
                self.notifyClient()
                return        
        else:
            self.warning('03')            
            self.notifyClient()                

    #------------------------------------------------------------------------------------------#
    def channel_off(self, channel):
        """
        Channel deaktivieren 
        @type     channel: string
        @param    channel: Kanal
        @rtype:   string
        @return:  Die Antwort des Liquidsoap-Servers        
        """        
        # internal channel name for playlist is 'common'
        if channel == 'playlist':
            channel = 'common'

        channels = self.sendLqcCommand(self.lqc, 'listChannels', False)

        index = channels.index(channel)            
        message = self.sendLqcCommand(self.lqc, 'deactivate', str(index))        
        if message.find('selected=false'):
            self.success()            
        else:
            self.warning('01')
        
        self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def channel_on(self, channel):
        """
        Channel aktivieren 
        @type     channel: string
        @param    channel: Kanal
        @rtype:   string
        @return:  Die Antwort des Liquidsoap-Servers               
        """                
        # Find channels
        if channel == 'playlist':
            channel = 'common'
        
        channels = self.sendLqcCommand(self.lqc, 'listChannels', False)
        
        index = channels.index(channel)        
        #a activate channel    
        message = self.sendLqcCommand(self.lqc, 'activate', str(index))        
        
        if message.find('selected=true'):
            self.success()
        else:
            self.warning('01')
        
        self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def channel_queue(self, channel, raw=False):
        """
        Channel Queue abrufen 
        @type     channel: string
        @param    channel: Kanal
        @type     raw:     boolean
        @param    raw:     Wenn true, Rückgabe als Python dict Object, andernfalls als JSON-String
        @rtype:   string/dict
        @return:  Der Channel Queue               
        """        
        data = {}
        
        # queue will return request id's (rids)
        
        message = self.sendLqcCommand(self.lqc, 'get_queue', channel)
              
        rids = message.strip().split(' ')
        data['tracks'] = []               
        for rid in rids:
            if rid != '':
                # get each rids metadata
                metadata = self.sendLqcCommand(self.lqc, 'getMetadata', rid)
                track = self._metadata_format(metadata)
                if not track.has_key('title'):
                    if track.has_key('location'):
                        track['title'] = os.path.basename(track['location'])
                    elif track.has_key('filename'):
                        track['title'] = os.path.basename(track['filename'])
                    else:
                        track['title'] = 'unknown' 
                        
                data['tracks'].extend([track])
        channels = self.sendLqcCommand(self.lqc, 'listChannels')   
        
        """
        now get channels state 
        self.lqc.status: ready=false volume=100% single=false selected=false remaining=0.00
        """        
        try:
            index = channels.index(channel)           
            status = self.sendLqcCommand(self.lqc, 'status', 'mixer', str(index + 1))            
            states = status.split(' ')            
            state_data = {}
            for state in states:
                item = state.split('=')
                if len(item) > 1:
                    state_data[item[0]] = item[1]
        except:
            state_data = {}
            self.error('01')
            self.notifyClient()
            return 
            
         
        data['state'] = state_data
        
        if raw:
            # return the list internal
            data['state'] = state_data            
            return data
        else:
            self.success('00', data)
            self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def channel_remove(self, channel, pos):
        """
        Channel-Eintrag löschen 
        @type     channel: string
        @param    channel: Kanal
        @type     pos:     int
        @param    pos:     Position des Eintrags                      
        """               
        # Es kann nur vom Secondary Queue gelöscht werden
        # Falls der Track im Primary Queue gelöscht werden soll, ist ein skip nötg  

        message = self.sendLqcCommand(self.lqc, 'get_queue', channel, 'secondary_queue')        
        rids = message.strip().split(' ')
        try:
            rid = rids[int(pos)-1]
        except:
            self.warning('02')
            self.notifyClient()
            return
        message = self.sendLqcCommand(self.lqc, 'remove', rid, channel)
        if message.find('OK') > -1:
            self.success()
        else:
            self.warning('01')

        self.notifyClient()          

    #------------------------------------------------------------------------------------------#
    def channel_seek(self, channel, duration):
        """
        Im aktuell spielenden Track auf dem Kanal <channel> <duration> Sekunden "vorspulen"
        @type     channel:  string
        @param    channel:  Kanal
        @type     duration: int
        @param    duration: Dauer in Sekunden               
        """                
        # Liquidsoap Kommando        
        data = self.sendLqcCommand(self.lqc, 'seek', duration, channel)
                
        # Resultate prüfen
        if self._check_result(data):        
                self.success('00', self.lq_error['value'])
        else:    
            self.warning('01')
        
        self.notifyClient()                        

    #------------------------------------------------------------------------------------------#
    def channel_skip(self, channel):
        """
        Kanal skippen 
        @type     channel:  string
        @param    channel:  Kanal      
        """                        
        # Liquidsoap Kommando
        channels = self.sendLqcCommand(self.lqc, 'listChannels')

        foundChannel = ''
        if not isinstance(channels, list):
            self.error('02')    
        else:        
            for index, item in enumerate(channels):
                if item == channel:
                    foundChannel = self.sendLqcCommand(self.lqc, 'skip', 'mixer', str(index + 1))
                    break
               
            if foundChannel.strip().find('OK') > -1:
                self.success()
            elif len(channels) < 1:
                self.warning('01')            
            else:
                self.error('03')                                
        
        self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def channel_volume(self, channel, volume):
        """
        Lautstärke auf Kanal <channel> setzen
        @type     channel:  string
        @param    channel:  Kanal
        @type     volume:   int
        @param    volume:  Lautstärke von 1-100               
        """        
                
        if channel == 'playlist':
            channel = 'common'
        # Liquidsoap Kommando
        channels = self.sendLqcCommand(self.lqc, 'listChannels', False)
                
        try: 
            index = channels.index(channel)
            if len(channel) < 1:
                self.warning('02')
        except:
            self.error('03')
            
        else:
            message = self.sendLqcCommand(self.lqc, 'volume', str(index), str(int(volume)))

            if message.find('volume=' + str(volume) + '%'):
                self.success('01', str(volume))
            else:
                self.warning('01')
        
        self.notifyClient()                 

    #------------------------------------------------------------------------------------------#
    def currentData(self):
        """
        Metadaten des gespielten Tracks im JSON-Format
        Beispiel: {"title": "Deserted Cities of the Heart", "filename": "/home/michel/Nas-audio/cream/the_very_best_of/17_Deserted_Cities_of_the_Heart.mp3", "source": "ch2", "on_air": "2014/07/23 23:46:37",  "rid": "2"}               
        """          
        # Liquidsoap Kommando
        message = self.sendLqcCommand(self.lqc, 'currentTrack')      
        
        rid = message.strip()        
        
        metadata = self.sendLqcCommand(self.lqc, 'getMetadata', rid)                    

        data = self._metadata_format(metadata)
        
        if data:
            self.success('00', simplejson.dumps(data))
        elif rid == '':
            self.warning('01')
        else:
            self.warning('02', rid)
        
        self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def get_channel_state(self, channel):        
        if channel == 'playlist':
            channel = 'common'
            
        channels = self.sendLqcCommand(self.lqc, 'listChannels', False)

        index = channels.index(channel)        
        state_data = {}
        try:
            index = channels.index(channel)           
            status = self.sendLqcCommand(self.lqc, 'status', 'mixer', str(index + 1))            
            states = status.split(' ')                        
            for state in states:
                item = state.split('=')
                if len(item) > 1:
                    state_data[item[0]] = item[1]
        except:
            state_data = {}
            self.error('01')
            self.notifyClient()
            return 
        self.success('00', simplejson.dumps(state_data))        
        self.notifyClient()                    

    #------------------------------------------------------------------------------------------#
    def help(self):        
        """
        Gibt die Hilfe aus                 
        """
        errNum = '11'
        try:
            file = open(os.path.dirname(os.path.abspath(__file__)) +  '/doc/comba.hlp', 'r')
            doc = file.read()
            return self.message(doc)
        except:
            self.warning('01')
            self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def listChannels(self):
        """
        Channels auflisten (Simple JSON)                      
        """           
        # Liquidsoap Kommando
        channels = self.sendLqcCommand(self.lqc, 'listChannels')      
        
        if not isinstance(channels, list):
            self.error('02')
        elif len(channels) < 1:            
            self.warning('01')
        else:
            self.success('00', channels)
        
        self.notifyClient()      

    #------------------------------------------------------------------------------------------#
    def playlist_data(self, raw=False):
        """
        Aktuelle Playlist Daten im JSON-Format                  
        """
        
        # Liquidsoap Kommando
        data = self.sendLqcCommand(self.lqc, 'playlistData')
        if not raw:
            self.success('00', simplejson.loads(data))
            self.notifyClient()
        else:
            return data     
           
    #------------------------------------------------------------------------------------------#
    def playlist_flush(self):
        """
        Aktuelle Playlist leeren           
        """ 
        data = self.sendLqcCommand(self.lqc, 'flush')               
        self.success('00') 
        self.notifyClient()        

    #------------------------------------------------------------------------------------------#
    def playlist_insert(self, uri, pos):
        """
        Track in die Playlist einfuegen                        
        """                        
        data = self.sendLqcCommand(self.lqc, 'insert', uri, pos)
        if not self._check_result(data):
            self.warning('01')
        else:
            self.success('00') 
        self.notifyClient()
        
    #------------------------------------------------------------------------------------------#
    def playlist_load(self, uri):
        """
        Playlist laden        
        @type   uri:   string
        @param  uri:   Uri der Playlist          
        """

        try:
            xml = urllib2.urlopen(uri).read().decode('utf8')

        except:
            try:
                xml = open(uri).read().decode('utf8')
            except:
                self.error('01', self.lq_error['message'])
                self.notifyClient()
                return

        (num, filename) = tempfile.mkstemp(suffix=".xspf")

        with codecs.open(filename, "w",encoding='utf8') as text_file:
            text_file.write(xml)

        playlist = parsexml(xml)


        if not isinstance(playlist, dict):
            self.error('02')
            self.notifyClient()
        else:
            self.sendLqcCommand(self.lqc, 'flush')
            data = self.sendLqcCommand(self.lqc, 'loadPlaylist', filename)

            if not self._check_result(data):
                self.error('01', self.lq_error['message'])
                     
            else:
                os.remove(filename)
                self._updateEventQueue(playlist)
                event = {'job':'loadplaylist', 'uri': uri}         
                self.messenger.fireEvent('loadplaylist', event, 'player')
                self.success('00') 
        
            self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def playlist_move(self, fromPos, toPos):
        """
        Playlist-Eintrag von Position fromPos nach Position toPos verschieben
        @type     fromPos: int
        @param    fromPos: die Position des Eintrags, der verschoben wird 
        @type     toPos:   int
        @param    toPos:   Zielposition                                       
        """               
        data = self.sendLqcCommand(self.lqc, 'move', str(int(fromPos)+1), str(int(toPos)+1))
            
        if not self._check_result(data):
            self.warning('01')
        else:
            self.success('00') 
        
        self.notifyClient()    
    
    #------------------------------------------------------------------------------------------#
    def playlist_pause(self):
        """
        Playlist pausieren
        """               

        data = self.sendLqcCommand(self.lqc, 'pause')
        
        if not self._check_result(data):
            self.info('01')
        else:
            self.success('00') 
        
        self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def playlist_stop(self):
        """
        Playlist stoppen - der Kanal wird deaktiviert
        """
        # Kanal 0 (Playlist) deaktivieren
        self.sendLqcCommand(self.lqc, 'deactivate', '0')

        data = self.sendLqcCommand(self.lqc, 'pause')

        if not self._check_result(data):
            self.info('01')
        else:
            self.success('00')

        self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def playlist_play(self, when='now'):
        """
        Playlist starten
        @type   when:   string
        @param  when:   Wenn "now" werden alle anderen Kanäle deaktiviert und geskipped                   
        """                       
        # Playlist Kanal aktivieren
        self.sendLqcCommand(self.lqc, 'activate', '0')
                
        if when == 'now':            
            # immediately skip all playing channels
            # and activate the playlist channel
            channels = self.sendLqcCommand(self.lqc, 'listChannels')
            if not isinstance(channels, list):
                self.error('03')
            elif len(channels) < 1:            
                self.warning('02')
            else:        
                for i in xrange(len(channels)):
                    status = self.sendLqcCommand(self.lqc, 'status', 'mixer', str(i + 1))
                    if "selected=true" in status:
                        status = self.sendLqcCommand(self.lqc, 'deactivate', str(i + 1))
                        status = self.sendLqcCommand(self.lqc, 'skip', 'mixer', str(i + 1))
                        self.sendLqcCommand(self.lqc, 'activate', '0')

        # send the play command
        data = self.sendLqcCommand(self.lqc, 'play')
        if not self._check_result(data):
            self.info('01')
        else:
            self.success('00')
        
        self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def playlist_push(self, uri):
        """
        Eine Uri in die Playlist einfügen
        @type   uri:   str
        @param  uri:   Die Uri
        """                       
        data = self.sendLqcCommand(self.lqc, 'push', uri)
        
        if not self._check_result(data):
            self.info('01')
        else:
            self.success('00') 
        
        self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def playlist_remove(self, pos):
        """
        Playlist-Eintrag löschen 
        @type     pos:     int
        @param    pos:     Position des Eintrags             
        """
        data = self.sendLqcCommand(self.lqc, 'remove', pos)

        if not self._check_result(data):
            self.info('01')
        else:
            self.success('00') 
        
        self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def playlist_seek(self, duration):
        """
        Im aktuell spielenden Track auf dem der Playlist "vorspulen"
        @type     duration: int
        @param    duration: Dauer in Sekunden     
        """                
        data = self.sendLqcCommand(self.lqc, 'seek', duration)
                
        # Resultate prüfen
        if self._check_result(data):        
                self.success('00', self.lq_error['value'])
        else:    
            self.warning('01')
        
        self.notifyClient()                         

    #------------------------------------------------------------------------------------------#
    def playlist_skip(self):
        """
        Playlist skippen 
        """
        data = self.sendLqcCommand(self.lqc, 'skip')                
        
        self.success('00')
        
        self.notifyClient()                         

    #------------------------------------------------------------------------------------------#
    def recorder_start(self):
        """
        Recorder starten
        @rtype:   string
        @return:  Die Antwort des Liquidsoap-Servers               
        """                                
        message = self.sendLqcCommand(self.lqcr, 'start_record')
        if message.strip() == 'OK':
            self.success('00')
        else:    
            self.warning('01')
                
        self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def recorder_stop(self):
        """
        Recorder stoppen  
        """
        message = self.sendLqcCommand(self.lqcr, 'stop_record')
                                 
        if message.strip() == 'OK':
            self.success('00')
        else:    
            self.warning('01')
                
        self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def recorder_stop(self):
        """
        Recorder stoppen  
        """
        message = self.sendLqcCommand(self.lqcr, 'stop_record')
                                 
        if message.strip() == 'OK':
            self.success('00')
        else:    
            self.warning('01')
                
        self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def recorder_data(self):
        """
        Status-Daten des Recorders
        Rückgabe-Beispiel: /var/audio/rec/2014-05-13/2014-05-13-22-00.wav,30 - Aufnahme von 30% der angegebenen Audiodatei     
        """   
        
        message = self.sendLqcCommand(self.lqcr, 'recorder_data')
        l = message.split(',')
        data = {}
        
        if not isinstance(l, list):
            data = {'file':'', 'recorded': ''}
            self.warning('01')
        else:
            data['file'] = l[0]
            if len(l) > 1:
                data['recorded'] = l[1]
            else:
                data['recorded'] = ''
            self.success('00', data) 
        
        self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def scheduler_reload(self):
        """
        Veranlasst den Scheduler zum Reload
        """
        process =  "combas.py"
        pids = psutil.get_pid_list()
        foundProcess = False  
        for pid in pids:
            cmd = psutil.Process(pid).cmdline
            if len(cmd) > 1:
                processName = cmd[1]
            else:
                processName = psutil.Process(pid).name    
    
            if processName.find(process) > 0:
                os.kill(pid,signal.SIGUSR1)                
                foundProcess = True  
                break
        if not foundProcess:
            return False
        else:
            return True

    #------------------------------------------------------------------------------------------#
    def scheduler_data(self):
        """
        Scheduler Config ausliefern
        """
        jobs = []
        
        try:
            # Das scheduler.xml laden            
            schedulerconfig = CombaSchedulerConfig(self.sender.schedule_config)
            jobs = schedulerconfig.getJobs()
        except:
            # Das scheint kein gültiges XML zu sein            
            self.warning('01', False)
                    
        self.success('00', simplejson.dumps(jobs))
        self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def scheduler_store(self, adminuser, adminpassword, json):
        """
        Scheduler Config zurückschreiben
        """   
        if not self.userdb.hasAdminRights(adminuser, adminpassword):        
            self.warning('01', False)
            self.notifyClient()
            return
        try:
            schedulerconfig = CombaSchedulerConfig(self.sender.schedule_config)
        except:
            self.warning('02', False)
            self.notifyClient()
        try:
            schedulerconfig.storeJsonToXml( base64.b64decode(json))
        except:
            self.warning('02', False)
            self.notifyClient()
        else:                    
            if self.scheduler_reload():
                self.success('00', True)
            else:
                self.warning('02', False)            
        self.notifyClient()

    #------------------------------------------------------------------------------------------#
    def setPassword(self, adminuser, adminpassword, username, password):
        """
        Ein Userpasswort setzen
        TODO: Passwörter verschlüsselt übertragen
        """
        if self.userdb.hasAdminRights(adminuser, adminpassword):
            self.userdb.setPassword(username, password)
            self.success('00', password)
        else:
            self.warning('01', False)

        self.notifyClient()
        self.sender.reload()

    #------------------------------------------------------------------------------------------#
    def addUser(self, adminuser, adminpassword, username):
        """
        Einen User hinzufügen
        TODO: Passwort verschlüsselt übertragen
        """
        if self.userdb.hasAdminRights(adminuser, adminpassword):
            password = ''.join(random.sample(string.lowercase+string.uppercase+string.digits,14))
            self.userdb.insertUser(username, password, 'user')
            self.success('00', password)
        # TODO admin rechte checken user und passwort setzen, passwort zurückgeben
        else:
            self.warning('01', False)

        self.notifyClient()
        self.sender.reload()

    #------------------------------------------------------------------------------------------#
    def delUser(self, adminuser, adminpassword, username):
        """
        Einen User löschen
        TODO: Passwort verschlüsselt übertragen
        """
        # TODO admin rechte checken user löschen
        if self.userdb.hasAdminRights(adminuser, adminpassword):
            self.userdb.delete(username)
            self.success('00', True)
        else:
            self.warning('01', False)

        self.notifyClient()
        self.sender.reload()

    #------------------------------------------------------------------------------------------#
    def getUserlist(self, adminuser, adminpassword):
        """
        Einen User löschen
        TODO: Passwort verschlüsselt übertragen
        """
        # TODO admin rechte checken user löschen
        if self.userdb.hasAdminRights(adminuser, adminpassword):
            userlist = self.userdb.getUserlist()
            self.success('00', simplejson.dumps(userlist))
        else:
            self.warning('01', False)

        self.notifyClient()


    #------------------------------------------------------------------------------------------#
    def _metadata_format(self, metadata):
        """
        Private: Vereinheitlicht die Metadaten von Playlist und anderen Kanälen und entfernt Anführungszeichen in den Feldern
        @rtype:   boolean/dict
        @return:  False/Metadaten
        """           
        mdata = {}  
        try:
            for key,val in metadata.items('root'):
                if key in self.knownfields:
                    mdata[key] = val.strip().replace('"', '')
            return mdata
        except:
            return False

    #------------------------------------------------------------------------------------------#
    def _getError(self, errornumber):
        """
        Privat: Ermittelt Fehlermeldung, Job-Name (Klassenmethode) und Fehlercode für den Job aus error/combac_error.js
        @type errornumber:  string
        @param errornumber: Die interne Fehlernummer der aufrufenden Methode  
        """        
        f = sys._getframe(2)
        
        job = f.f_code.co_name
        data = {'message':'', 'job':job, 'code':'unknown'}
        if self.errorData.has_key(job):
            errMsg = self.errorData[job][errornumber]
            errID = self.errorData[job]['id'] + str(errornumber) 
            args = {x:f.f_locals[x] if not x == 'self' else '' for x in f.f_code.co_varnames[:f.f_code.co_argcount]}
            
            for key in args.keys():            
                errMsg = errMsg.replace('::' + key + '::', str(args[key]))
            
            data['message'] = errMsg
            data['job'] = job
            data['code'] = errID
             
        return data

    #------------------------------------------------------------------------------------------#
    def success(self, errnum='00', value='', section='main'):
        """
        Erfolgsmeldung loggen
        @type errnum:    string
        @param errnum:   Errornummer der aufrufenden Funktion
        @type value:     string
        @param value:    Optionaler Wert 
        @type section:   string
        @param section:  Gültigkeitsbereich        
        """
        error = self._getError(errnum)
        self.job_result = {'message':error['message'], 'code':error['code'], 'success':'success', 'job':error['job'], 'value':value, 'section':section}
        self.messenger.send(error['message'], error['code'], 'success', error['job'], value, section)

    #------------------------------------------------------------------------------------------#
    def info(self, errnum='01', value='', section='main'):                        
        """
        Info loggen
        @type errnum:    string
        @param errnum:   Errornummer der aufrufenden Funktion
        @type value:     string
        @param value:    Optionaler Wert 
        @type section:   string
        @param section:  Gültigkeitsbereich                
        """
        error = self._getError(errnum)
        self.job_result = {'message':error['message'], 'code':error['code'], 'success':'info', 'job':error['job'], 'value':value, 'section':section}        
        self.messenger.send(error['message'], error['code'], 'info', error['job'], value, section)

    #------------------------------------------------------------------------------------------#
    def warning(self, errnum='01', value='', section='main'):
        """
        Warnung loggen
        @type errnum:    string
        @param errnum:   Errornummer der aufrufenden Funktion
        @type value:     string
        @param value:    Optionaler Wert 
        @type section:   string
        @param section:  Gültigkeitsbereich                
        """        
        error = self._getError(errnum)
        self.job_result = {'message':error['message'], 'code':error['code'], 'success':'warning', 'job':error['job'], 'value':value, 'section':section}
        self.messenger.send(error['message'], error['code'], 'warning', error['job'], value, section)

    #------------------------------------------------------------------------------------------#
    def error(self, errnum='01', value='', section='main'):
        """
        Error loggen
        @type errnum:    string
        @param errnum:   Errornummer der aufrufenden Funktion
        @type value:     string
        @param value:    Optionaler Wert 
        @type section:   string
        @param section:  Gültigkeitsbereich                
        """        
        error = self._getError(errnum)
        self.job_result = {'message':error['message'], 'code':error['code'], 'success':'error', 'job':error['job'], 'value':value, 'section':section}
        self.messenger.send(error['message'], error['code'], 'error', error['job'], value, section)

    #------------------------------------------------------------------------------------------#
    def fatal(self, errnum='01', value='', section='main'):
        """
        Fatal error loggen
        @type errnum:    string
        @param errnum:   Errornummer der aufrufenden Funktion
        @type value:     string
        @param value:    Optionaler Wert 
        @type section:   string
        @param section:  Gültigkeitsbereich                
        """
        error = self._getError(errnum)
        self.job_result = {'message':error['message'], 'code':error['code'], 'success':'fatal', 'job':error['job'], 'value':value, 'section':section}
        self.messenger.send(error['message'], error['code'], 'fatal', error['job'], value, section)    

    #------------------------------------------------------------------------------------------#
    def notifyClient(self):
        """
        Eine Nachricht als JSON-String an den Client senden
        """
        if not self.is_intern:                        
            self.message(simplejson.dumps(self.job_result))

    #------------------------------------------------------------------------------------------#
    def sendLqcCommand(self, lqs_instance, command, *args):
        """
        Ein Kommando an Liquidsoap senden
        @type  lqs_instance: object
        @param lqs_instance: Instanz eines Liquidsoap-Servers - recorder oder player
        @type  command:      string
        @param command:      Ein Funktionsname aus der Liquidsoap-Klasse
        @type args:          list
        @param args:         Parameterliste
        @rtype:              string
        @return:             Antwort der Liquidsoap-Instanz
        """
        try:
        # Verbindung herstellen
            lqs_instance.connect()
        except:
            # Verbindung gescheitert - Fehler an Client
            if command.find('record') > -1:
                self.fatal('02')
            else:
                self.fatal('01') 
                
            self.notifyClient()
            # Instanz/Thread zerstören - aufrufende Funktion wird nicht weiter abgearbeitet
            del self
        else:
            # Funktion des Liquidsoap Servers zusammenbasteln 
            func = getattr(lqs_instance, command)
            result = func(*args)             
            return result
            
    #------------------------------------------------------------------------------------------#
    def _check_result(self, result):
        """
        Fehlerbehandlung
        @type     result: string
        @param    result: Ein Json-String
        """
        self.lq_error = simplejson.loads(result)
        
        try:
            if self.lq_error['success'] == 'true':
                return True
            else:
                return False
        except:
            return False

    #------------------------------------------------------------------------------------------#
    def _updateEventQueue(self, playlist):
        """
        Playlist Eventqueue updaten
        @type playlist: dict 
        @param playlist: Playlist 
        """
        # eventuell noch bestehende Events im Queue löschen
        self.messenger.queueRemoveEvents('playtrack', 'player')
        # Für jeden Tack einen Event ankündigen
        for track in playlist['playlist']['trackList']['track']:

            if track.has_key('time') and track.has_key('start'):
                starts = str(track['start'] + 'T' + track['time'])
                event = {'job':'play', 'location': track['location'],'length': track['length'], 'date': track['start'], 'time': track['time']}
                self.messenger.queueAddEvent('playtrack', starts, event, 'player')