Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • aura/engine
  • hermannschwaerzler/engine
  • sumpfralle/aura-engine
3 results
Show changes
Showing
with 3736 additions and 0 deletions
from xml.dom.minidom import parse
import datetime
from datetime import timedelta
import simplejson
from xml.etree import ElementTree
class NotTextNodeError(BaseException):
pass
class AuraSchedulerConfig():
def __init__(self, xmlpath):
self.jobs = {}
self.filename = xmlpath
self.playperiods = []
self.recordperiods = []
self.hasinstance = False
self.until = None
# -----------------------------------------------------------------------#
def getPlayPeriods(self):
if not self.hasinstance:
self.getJobs()
return self.playperiods
# -----------------------------------------------------------------------#
def getRecordPeriods(self):
if not self.hasinstance:
self.getJobs()
return self.recordperiods
# -----------------------------------------------------------------------#
def getJobs(self):
self.hasinstance = True
self.loadXml()
for job in self.jobs:
if 'job' not in job:
continue;
if 'until' not in job:
job['until'] = ''
if 'day' not in job:
job['day'] = 'all'
# self.jobs.sort(cmp=lambda x,y: cmp(x['time'], y['time']))
# self.jobs.sort(cmp=lambda x,y: cmp(x['day'], y['day']))
self.jobs.sort(key=lambda job: job['time'])
self.jobs.sort(key=lambda job: job['day'])
for index, job in enumerate(self.jobs):
if job['job'] == 'play_playlist':
job['duration'] = self._calcDuration(job['time'], job['until'])
self.playperiods.append({'from': job['time'],'until': job['until'], 'duration': job['duration']})
day = None
if 'day' in job:
day = job['day']
self.addPlaylistLoadJob(job['time'], job['until'], day)
if job['job'] == 'start_recording':
job['duration'] = self._calcDuration(job['time'], job['until'])
self.recordperiods.append({'from': job['time'],'until': job['until'], 'duration': job['duration']})
return self.jobs
# -----------------------------------------------------------------------#
def addPlaylistLoadJob(self, playTime, untilTime, day=None):
job = {}
playStart = datetime.datetime.strptime('1901-01-01T' + playTime,'%Y-%m-%dT%H:%M');
loadTime = playStart - timedelta(minutes=3)
loadTime = loadTime.strftime('%H:%M')
job['time'] = loadTime
job['from'] = playTime
job['until'] = untilTime
job['job'] = 'load_playlist'
if day and not day == 'all' and loadTime > playTime:
day = int(day)
day = 6 if day == 0 else day - 1
job['day'] = str(day)
self.jobs.append(job)
# -----------------------------------------------------------------------#
def storeJsonToXml(self, json):
try:
jobs = simplejson.loads(json)
except:
return False
xml = '<?xml version="1.0" encoding="UTF-8"?>'+"\n"
xml += '<Config>'+"\n";
xml += ' <Jobs multiple="true">'+"\n";
xmlend = ' </Jobs>'+"\n";
xmlend += '</Config>';
for job in jobs:
xml+= ' <job>'+"\n";
for key in job.keys():
xml+= ' <'+key+'>'+str(job[key])+'</'+key+'>'+"\n"
if not job.has_key('params'):
xml+= ' <params></params>'+"\n"
if not job.has_key('day'):
xml+= ' <day>all</day>'+"\n"
xml+= ' </job>'+"\n"
# validate xml
try:
x = ElementTree.fromstring(xml+xmlend)
except:
return False
else:
try:
file = open(self.filename, "w")
file.write(xml+xmlend)
file.close()
except:
return False
else:
return True
# -----------------------------------------------------------------------#
def loadXml(self):
dom = parse(self.filename)
config = self.nodeToDic(dom)
self.jobs = config['Config']['Jobs']
# -----------------------------------------------------------------------#
def getTextFromNode(self, node):
t = ""
for n in node.childNodes:
if n.nodeType == n.TEXT_NODE:
t += n.nodeValue
else:
raise NotTextNodeError
return t
# -----------------------------------------------------------------------#
def nodeToDic(self, node):
dic = {}
for n in node.childNodes:
if n.nodeType != n.ELEMENT_NODE:
continue
if n.getAttribute("multiple") == "true":
# node with multiple children:
# put them in a list
l = []
for c in n.childNodes:
if c.nodeType != n.ELEMENT_NODE:
continue
l.append(self.nodeToDic(c))
dic.update({n.nodeName: l})
continue
try:
text = self.getTextFromNode(n)
except NotTextNodeError:
# 'normal' node
dic.update({str(n.nodeName): self.nodeToDic(n)})
continue
# text node
dic.update({str(n.nodeName): str(text)})
continue
return dic
# -----------------------------------------------------------------------#
def in_timeperiod(self, now, job):
if 'until' not in job or not job['until']:
print("not in timeperiod")
return False
(hour1, minute1) = job['time'].split(':')
(hour2, minute2) = job['until'].split(':')
if job['time'] > job['until']:
print("in time period. time greater than until")
return datetime.time(hour=int(hour1), minute=int(minute1)) \
<= now.time()
else:
print("in time period. until greater than time")
return datetime.time(hour=int(hour1), minute=int(minute1)) \
<= now.time() \
<= datetime.time(hour=int(hour2), minute=int(minute2))
# -----------------------------------------------------------------------#
def _calcDuration(self, timestring1, timestring2):
"""Berechnet Zeit in Sekunden aus zwei Time-Strings
"""
ftr = [3600, 60, 1]
sec1 = sum([a * b for a, b in zip(ftr, map(int, timestring1.split(':')))])
sec2 = sum([a * b for a, b in zip(ftr, map(int, timestring2.split(':')))])
offset = 0 if sec2 > sec1 else 86400
return (sec2 + offset) - sec1
# -----------------------------------------------------------------------#
def find_next(self, items, index, key, value):
for idx, item in enumerate(items):
if idx <= index:
continue
if item[key] == value:
return idx
return self.find_next(items,0,key,value)
__author__ = 'gg'
# -*- coding: utf-8 -*-
import datetime, os, urllib, sys
import time
import decimal
from sqlalchemy import orm, func, Boolean, Column, Date, DateTime, Float, Integer, String, Text, Time, ForeignKey, ForeignKeyConstraint
from sqlalchemy.orm import relationship
from sqlalchemy.sql.expression import false
from libraries.database.database import db
class AuraDatabaseModel:
def store(self, add=False, commit=False):
#obj_session = db.session.object_session(self)
#if obj_session is not None:
# obj_session.add(self)
# if commit:
# obj_session.commit()
if add:
db.session.add(self)
if commit:
db.session.commit()
def delete(self, commit=False):
#obj_session = db.session.object_session(self)
#if obj_session is not None:
# obj_session.delete(self)
# if commit:
# obj_session.commit()
db.session.delete(self)
if commit:
db.session.commit()
def _asdict(self):
return self.__dict__
@staticmethod
def recreate_db(systemexit = False):
manualschedule = Schedule()
manualschedule.schedule_id = 0
manualschedule.show_name = "Manual Show"
# fallback_trackservice_schedule = TrackServiceSchedule()
# fallback_trackservice_schedule.ts_schedule_id = 0
print("Recreating Database...")
db.drop_all()
print("all dropped. creating...")
db.create_all()
print("inserting manual scheduling possibility and fallback trackservice schedule")
db.session.add(manualschedule)
# db.session.add(fallback_trackservice_schedule)
print("all created. commiting...")
db.session.commit()
print("Database recreated!")
if systemexit:
sys.exit(0)
# ------------------------------------------------------------------------------------------ #
class Schedule(db.Model, AuraDatabaseModel):
"""
One specific Schedule for a show on a timeslot
"""
__tablename__ = 'schedule'
# primary and foreign keys
schedule_id = Column(Integer, primary_key=True, autoincrement=False)
show_id = Column(Integer) # well, not needed..
schedule_start = Column(DateTime) # can be null due to manual entries
schedule_end = Column(DateTime) # can be null due to manual entries
show_name = Column(String(256))
show_hosts = Column(String(256))
rtr_category = Column(String(256))
comment = Column(String(512))
languages = Column(String(256))
type = Column(String(256))
category = Column(String(256))
topic = Column(String(256))
musicfocus = Column(String(256))
is_repetition = Column(Boolean())
playlist_id = Column(Integer)
timeslot_fallback_id = Column(Integer)
show_fallback_id = Column(Integer)
station_fallback_id = Column(Integer)
def get_length(self):
sec1 = int(datetime.datetime.strptime(self.start[0:16].replace(" ", "T"), "%Y-%m-%dT%H:%M").strftime("%s"))
sec2 = int(datetime.datetime.strptime(self.end[0:16].replace(" ", "T"), "%Y-%m-%dT%H:%M").strftime("%s"))
len = sec2 - sec1
return len
# ------------------------------------------------------------------------------------------ #
class ScheduleEntry(db.Model, AuraDatabaseModel):
"""
One schedule can have multiple entries
"""
__tablename__ = 'schedule_entry'
# primary and foreign keys
playlist_id = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
entry_num = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
schedule_id = Column(Integer, ForeignKey("schedule.schedule_id"))
entry_start = Column(DateTime) # can be null due to fallback playlists
entry_end = Column(DateTime) # can be null due to fallback playlists
source = Column(String(256))
artist = Column(String(256))
track = Column(String(256))
albumname = Column(String(256))
genre = Column(String(256))
tracknum = Column(String(256))
cdnum = Column(String(256))
year = Column(Integer())
volume = Column(Integer, default=100)
is_fallback = Column(Boolean, default=False)
entry_start_unix = 0
entry_end_unix = 0
programme_index = -1
schedule = relationship("Schedule", foreign_keys=[schedule_id], lazy="joined")
def __init__(self, **kwargs):
super(ScheduleEntry, self).__init__(**kwargs)
self.calc_unix_times()
@orm.reconstructor
def calc_unix_times(self):
if self.entry_start is not None:
self.entry_start_unix = time.mktime(self.entry_start.timetuple())
if self.entry_end is not None:
self.entry_end_unix = time.mktime(self.entry_end.timetuple())
# ------------------------------------------------------------------------------------------ #
@staticmethod
def select_all():
# when deleting all entries, and fetching new programmes, the entries are stored and commited in the code.
# but sqlalchemy thinks somehow it is not commit and returns an empty set
#print("WARNING: broadcasts.py This commit before SELECT is a BAND-AID & UGLY-HACK. Why the hell is there a transaction pending and not commited?")
#db.session.commit()
# fetching all entries
all_entries = db.session.query(ScheduleEntry).filter(ScheduleEntry.is_fallback == false()).order_by(ScheduleEntry.entry_start).all()
# BAND-AID debug output. The model and db session are different. crap
#print("broadcasts.py SELECT ALL ScheduleEntry.q.session == db.session?")
#print(ScheduleEntry.query.session == db.session)
#print("broadcasts.py SELECT ALL ScheduleEntry.q.s.conn == db.s.conn?")
#print(ScheduleEntry.query.session.connection() == db.session.connection())
cnt = 0
for entry in all_entries:
entry.programme_index = cnt
cnt = cnt + 1
return all_entries
@staticmethod
def select_next_manual_entry_num():
max_manual_entry_num = db.session.query(func.max(ScheduleEntry.entry_num)).filter(ScheduleEntry.schedule_id == 0).first()
print(max_manual_entry_num)
if max_manual_entry_num[0] is None:
return 0
else:
return int(max_manual_entry_num[0])+1
# print("returning", res)
# return res
# ------------------------------------------------------------------------------------------ #
@staticmethod
def upcoming(datefrom=datetime.datetime.now()):
# damn BAND-AID
# db.session.commit()
upcomingtracks = db.session.query(ScheduleEntry).filter(ScheduleEntry.start > datefrom).all()
#upcomingtracks = ScheduleEntry.query.filter(ScheduleEntry.start > datefrom).all()
return upcomingtracks
# ------------------------------------------------------------------------------------------ #
@staticmethod
def select_one(playlist_id, entry_num):
return db.session.query(ScheduleEntry).filter(ScheduleEntry.playlist_id == playlist_id, ScheduleEntry.entry_num == entry_num).first()
# ------------------------------------------------------------------------------------------ #
def __str__(self):
return "ScheduleEntry starts @ " + ScheduleEntry.entry_start + " and ends @ " + ScheduleEntry.entry_end + " and plays " + ScheduleEntry.source
# ------------------------------------------------------------------------------------------ #
class TrackService(db.Model, AuraDatabaseModel):
__tablename__ = 'trackservice'
trackservice_id = Column(Integer, primary_key=True, autoincrement=True)
playlist_id = Column(Integer, nullable=False)
entry_num = Column(Integer, nullable=False)
source = Column(String(255), nullable=False)
start = Column(DateTime, nullable=False, default=func.now())
__table_args__ = (
ForeignKeyConstraint(['playlist_id', 'entry_num'], ['schedule_entry.playlist_id', 'schedule_entry.entry_num']),
)
#schedule = relationship("Schedule", foreign_keys=[schedule_id], lazy="joined")
# trackservice_entry = relationship("ScheduleEntry", foreign_keys=[playlist_id, entry_num], lazy="joined")
schedule_entry = relationship("ScheduleEntry", primaryjoin="and_(TrackService.playlist_id==ScheduleEntry.playlist_id, TrackService.entry_num==ScheduleEntry.entry_num)", lazy="joined")
@staticmethod
def select_one(trackservice_id):
return db.session.query(TrackService).filter(TrackService.trackservice_id == trackservice_id).first()
# ------------------------------------------------------------------------------------------ #
# class TrackServiceSchedule(db.Model, AuraDatabaseModel):
# """
# Trackservice is tracking every schedule.
# """
# __tablename__ = 'trackservice_schedule'
#
# # primary and foreign keys
# ts_schedule_id = Column(Integer, primary_key=True, autoincrement=True)
# schedule_id = Column(Integer, ForeignKey("schedule.schedule_id"))
#
# schedule = relationship("Schedule", foreign_keys=[schedule_id], lazy="joined")
#
# # ------------------------------------------------------------------------------------------ #
# @staticmethod
# def select_one(schedule_id):
# # damn BAND-AID
# # db.session.commit()
#
# return db.session.query(ScheduleEntry).filter(TrackServiceSchedule.schedule_id == schedule_id).first()
#
# # ------------------------------------------------------------------------------------------ #
# class TrackServiceScheduleEntry(db.Model, AuraDatabaseModel):
# """
# And a schedule can have multiple entries
# """
# __tablename__ = 'trackservice_entry'
#
# # primary and foreign keys. the foreign keys here can be null, because of fallback stuff
# ts_entry_id = Column(Integer, primary_key=True, autoincrement=True)
# ts_schedule_id = Column(Integer, ForeignKey("trackservice_schedule.ts_schedule_id"), nullable=True)
# playlist_id = Column(Integer, nullable=True)
# entry_num = Column(Integer, nullable=True)
#
# fallback = Column(Boolean, default=False)
# fallback_start = Column(DateTime, nullable=True, default=None)
# source = Column(String(256), nullable=True, default=None)
#
# # foreign key definitions
# __table_args__ = (
# ForeignKeyConstraint(['playlist_id', 'entry_num'], ['schedule_entry.playlist_id', 'schedule_entry.entry_num']),
# )
#
# trackservice_schedule = relationship("TrackServiceSchedule", foreign_keys=[ts_schedule_id], lazy="joined")
# #trackservice_entry = relationship("ScheduleEntry", foreign_keys=[playlist_id, entry_num], lazy="joined")
# trackservice_entry = relationship("ScheduleEntry", primaryjoin="and_(TrackServiceScheduleEntry.playlist_id==ScheduleEntry.playlist_id, TrackServiceScheduleEntry.entry_num==ScheduleEntry.entry_num)" , lazy="joined")
#
# @staticmethod
# def select_all():
# return db.session.query(TrackServiceScheduleEntry).filter().all()
#AuraDatabaseModel.recreate_db(True)
import datetime, os, urllib
from sqlalchemy import Boolean, Column, Date, DateTime, Float, Integer, String, Text, Time, ForeignKey
from libraries.database.database import db
# ------------------------------------------------------------------------------------------ #
class BroadcastModel:
def store(self, commit=False):
db.session.add(self)
if commit:
self.commit()
def commit(self):
db.session.commit()
# ------------------------------------------------------------------------------------------ #
class Format(db.Model, BroadcastModel):
"""
ID of show format # Sendungsformat
"""
__tablename__ = 'format'
id = Column(Integer, primary_key=True)
name = Column(String(255))
# ------------------------------------------------------------------------------------------ #
class BroadcastPeriod(db.Model, BroadcastModel):
"""
Daten werden noch in der scheduling.xml gehalten
"""
__tablename__ = 'broadcast_period'
identifier = Column(Integer, primary_key=True)
start = Column(DateTime)
end = Column(DateTime)
meta = {
'ordering': ['+start']
}
# ------------------------------------------------------------------------------------------ #
class BroadcastEvent(db.Model, BroadcastModel):
"""
Definiert eine Sendung mit Start- und End-Datum
"""
__tablename__ = 'broadcast_event'
identifier = Column(Integer, primary_key=True, autoincrement=True) # Unique (o)id
id_ext = Column(String(30)) #
location = Column(String(512)) # archived audio file, will be available when the broadcast event is over
reccurrence_id = Column(Integer) # Unique id of event to be repeated
duration = Column(Integer) # duration in seconds
start = Column(DateTime, nullable=False, default=datetime.datetime.now()) # start date
end = Column(DateTime, nullable=False, default=datetime.datetime.now()+datetime.timedelta(minutes=60)) # end date
rerun = Column(Boolean, default=False) # true, if the event is a rerun
replay_of_datetime = Column(DateTime)
replay_of = Column(Integer, ForeignKey('broadcast_event.identifier'))
programme_id = Column(String(100))
station_name = Column(String(100))
station_id = Column(String(100))
title = Column(String(100))
subject = Column(String(200))
description = Column(String(300))
overwrite_event = Column(Integer, ForeignKey('broadcast_event.identifier'))
state = Column(String(50), default='created')
created = Column(DateTime, default=datetime.datetime.now)
modified = Column(DateTime, default=datetime.datetime.now)
modified_by = Column(String(50))
text = Column(String(400))
#data = DictField(required=False)
meta = {
'ordering': ['+start'],
'indexes': [
{'fields': ['title', "subject"],
'default_language': 'german',
'weight': {'title': 10, 'subject': 2}
}
]
}
def fileExists(self):
return os.path.exists(str(self.location).replace('file://', ''))
# ------------------------------------------------------------------------------------------ #
class BroadcastEventTrack(db.Model, BroadcastModel):
"""
Track, der einem BroadcastEvent zugeordnet ist
"""
__tablename__ = 'broadcast_event_track'
identifier = Column(Integer, primary_key=True) # Unique id
location = Column(String(512)) # audio location (url or file)
length = Column(Float) # duration in seconds
start = Column(DateTime) # start date
end = Column(DateTime) # end date
record_at = Column(DateTime) # The date on which the recording has to start.
broadcast_event = Column(Integer, ForeignKey('broadcast_event.identifier')) # the BroadcastEvent the track refers to
meta = {
'ordering': ['+start']
}
def isLast(self):
return self == BroadcastEventTrack.objects(broadcast_event=self.broadcast_event).order_by('-start').first()
def fileExists(self):
return os.path.exists(str(self.location).replace('file://', ''))
def totalLength(self):
tracks = BroadcastEventTrack.objects(broadcast_event=self.broadcast_event)
length = 0
for track in tracks:
length = length + track.length if track.length else length
return length#
# ------------------------------------------------------------------------------------------ #
class BroadcastEventOverride(db.Model, BroadcastModel): # Base
"""
Ein Track, der in die Sendeautomatisierung eingeblendet wird
Muss manuell erstellt werden
"""
__tablename__ = 'broadcast_event_override'
identifier = Column(Integer, primary_key=True) # Unique id
location = Column(String(512)) # audio location (url or file)
mimetype = Column(String(100)) # audio mime
bitrate = Column(Integer) # bitrate
channels = Column(Integer) # number of channels
length = Column(Float) # duration in seconds
start = Column(DateTime) # start date
end = Column(DateTime) # end date
seek = Column(Integer) # TODO: seconds to seek into the audio
ordering = Column(Integer, default=0) # track sorting
#data = DictField(required=False) # additional data
broadcast_event = Column(Integer, ForeignKey('broadcast_event.identifier')) # the BroadcastEvent the track refers to
meta = {
'ordering': ['+ordering']
}
def nextOrdering(self):
"""
Return number of next track in tracklist
@return: int ordering
"""
lastOverride = BroadcastEventOverride.objects(broadcast_event=self.broadcast_event)\
.order_by('-ordering')\
.first()
if lastOverride:
return lastOverride.ordering + 1
else:
return 1
def filled(self):
"""
what percent of the broadcast event is allready filled with audio
@return: percent filled with audio
"""
object = self.broadcast_event
parent_total = (object.end - object.start).total_seconds()
proc = 0
try:
proc = (self.totalLength() / parent_total) * 100
except:
proc = 0
return int(proc)
def totalLength(self):
"""
Get the total length of audio in the track list
@return: total length of audio
"""
tracks = BroadcastEventOverride.objects(broadcast_event=self.broadcast_event)
length = 0
for track in tracks:
if track.length:
length = length + track.length
return length
def fileExists(self):
request = urllib.Request(self.location)
request.get_method = lambda: 'HEAD'
try:
response = urllib.urlopen(request)
return True
except:
return False
from sqlalchemy.ext.declarative import declarative_base
from libraries.base.config import ConfigReader
from flask_sqlalchemy import SQLAlchemy
from flask_babel import Babel
from flask import Flask
Base = declarative_base()
#### load config ####
config = ConfigReader()
config.loadConfig()
#### read config ####
install_dir = config.get(str("install_dir"))
db_name = config.get(str("db_name"))
db_user = config.get(str("db_user"))
db_pass = config.get(str("db_pass"))
db_host = config.get(str("db_host"))
#### create database conn ####
app = Flask(__name__, template_folder=install_dir+'/modules/web/templates')
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://"+db_user+":"+db_pass+"@"+db_host+"/"+db_name+"?charset=utf8"
app.config['BABEL_DEFAULT_LOCALE'] = 'de'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
babel = Babel(app)
# -*- coding: utf-8 -*-
import redis
import time
import datetime
import simplejson
import re
import uuid
class RedisStateStore(object):
"""Store and get Reports from redis"""
def __init__(self, **redis_kwargs):
"""The default connection parameters are: host='localhost', port=6379, db=0"""
self.db= redis.Redis()
self.channel = '*'
self.section = '*'
self.separator = '_'
self.daily = False
# ------------------------------------------------------------------------------------------ #
def set_channel(self, channel):
"""
Kanal setzen
@type channel: string
@param channel: Kanal
"""
self.channel = channel
# ------------------------------------------------------------------------------------------ #
def set_section(self, section):
"""
Sektion setzen
@type section: string
@param section: Sektion
"""
self.section = section
# ------------------------------------------------------------------------------------------ #
def set_alive_state(self):
"""
Alive Funktion - alle 20 Sekunden melden, dass man noch am Leben ist
"""
self.set_state('alive', 'Hi', 21)
# ------------------------------------------------------------------------------------------ #
def get_alive_state(self, channel):
"""
Alive Status eines Channels ermitteln
@type channel: string
@param channel: der Channel
@rtype: string/None
@return: Ein String, oder None, bei negativem Ergebnis
"""
return self.get_state('alive', channel)
# ------------------------------------------------------------------------------------------ #
def set_state(self, name, value, expires=None, channel=None):
"""
Setzt einen Status
@type name: string
@param name: Name des state
@type value: string
@param value: Wert
@type channel: string
@param channel: Kanal (optional)
"""
if not channel:
channel = self.channel
key = self.__create_key__(channel + 'State', name)
if value == "":
self.db.delete(key)
else:
# publish on channel
message = simplejson.dumps({'eventname':name, 'value': value})
self.db.publish(channel + 'Publish', message)
# store in database
self.db.set(key, value)
if(expires):
self.db.expire(key, 21)
# ------------------------------------------------------------------------------------------ #
def get_state(self, name, channel):
"""
Holt einen Status
@type name: string
@param name: Name des state
@type channel: string
@param channel: Kanal (optional)
"""
key = self.__create_key__(channel + 'State', name)
return self.db.get(key)
# ------------------------------------------------------------------------------------------ #
def queue_add_event(self, eventtime, name, value, channel=None):
"""
Kündigt einen Event an
@type eventtime: string
@param eventtime: Datum und Zeit des events
@type name: string
@param name: Name des Events
@type value: dict
@param value: Werte
@type channel: string
@param channel: Kanal (optional)
"""
timeevent = datetime.datetime.strptime(eventtime[0:16],"%Y-%m-%dT%H:%M")
expire = int(time.mktime(timeevent.timetuple()) - time.time()) + 60
self.__set_event__(name, eventtime, value, 'Evqueue', 'evqueue', expire, channel)
# ------------------------------------------------------------------------------------------ #
def queue_remove_events(self, name=None, channel=None):
"""
Löscht Events
@type name: string
@param name: Name des Events
@type channel: string
@param channel: Kanal (optional)
"""
query = channel + 'Evqueue_' if channel else '*Evqueue_'
query = query + '*_' + name if name else query + '*_*'
keys = self.db.keys(query)
for delkey in keys:
self.db.delete(delkey)
# ------------------------------------------------------------------------------------------ #
def fire_event(self, name, value, channel=None):
"""
Feuert einen Event
@type name: string
@param name: Name des Events
@type value: dict
@param value: Werte
@type channel: string
@param channel: Kanal (optional)
"""
eventtime = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M")
self.__set_event__(name, eventtime, value, 'Event', 'events', 60, channel)
# ------------------------------------------------------------------------------------------ #
def __set_event__(self, name, eventtime, value, type, namespace, expire, channel=None):
"""
Feuert einen Event
@type eventtime: string
@param eventtime: Datum und Zeit des events
@type value: dict
@param value: Werte
@type channel: string
@param channel: Kanal (optional)
"""
if not channel:
channel = self.channel
timeevent = datetime.datetime.strptime(eventtime[0:16],"%Y-%m-%dT%H:%M")
key = self.__create_key__(channel + type, eventtime, name)
value['starts'] = eventtime[0:16]
value['eventchannel'] = channel
value['eventname'] = name
self.db.hset(key, namespace, value)
self.db.expire(key, expire)
# ------------------------------------------------------------------------------------------ #
def get_event_queue(self, name=None, channel=None):
"""
Holt events eines Kanals
@type channel: string
@param channel: Kanal (optional)
@rtype: list
@return: Liste der Events
"""
query = channel + 'Evqueue_' if channel else '*Evqueue_'
query = query + '*_' + name if name else query + '*_*'
keys = self.db.keys(query)
keys.sort()
entries = self.__get_entries__(keys, 'evqueue')
return entries
# ------------------------------------------------------------------------------------------ #
def get_events(self, name=None, channel=None):
"""
Holt events eines Kanals
@type channel: string
@param channel: Kanal (optional)
@rtype: list
@return: Liste der Events
"""
query = channel + 'Event_' if channel else '*Event_'
query = query + '*_' + name if name else query + '*_*'
keys = self.db.keys(query)
keys.sort()
entries = self.__get_entries__(keys, 'events')
return entries
# ------------------------------------------------------------------------------------------ #
def get_next_event(self, name=None, channel=None):
"""
Holt den aktuellsten Event
@type channel: string
@param channel: Kanal (optional)
@rtype: dict/boolean
@return: ein Event oder False
"""
events = self.get_event_queue(name, channel)
if len(events) > 0:
result = events.pop(0)
else:
result = False
return result
# ------------------------------------------------------------------------------------------ #
def store(self, level, value):
"""
Hash speichern
@type level: string
@param level: der errorlevel
@type value: dict
@param value: Werte als dict
"""
microtime = str(time.time())
value['microtime'] = microtime
value['level'] = level
key = self.__create_key__(self.channel, self.section, level, microtime, str(uuid.uuid1()))
self.db.hset(key, self.channel, value)
self.db.expire(key, 864000)
# ------------------------------------------------------------------------------------------ #
def __get_keys__(self, level ='*'):
"""
Redis-Keys nach Suchkriterium ermitteln
@type level: string
@param level: einen Errorlevel filtern
@rtype: list
@return: Die Keys auf die das Suchkriterium zutrifft
"""
key = self.__create_key__(self.channel, self.section, level)
microtime = str(time.time())
search = microtime[0:4] + '*' if self.daily else '*'
return self.db.keys(key + self.separator + '*')
# ------------------------------------------------------------------------------------------ #
def __create_key__(self, *args):
"""
Key erschaffen - beliebig viele Argumente
@rtype: string
@return: Der key
"""
return self.separator.join(args)
def get_entries(self, level ='*'):
"""
Liste von Hashs nach Suchkriterium erhalten
@type level: string
@param level: einen Errorlevel filtern
@rtype: list
@return: Redis Hashs
"""
def tsort(x,y):
if float(x.split('_',4)[3]) > float(y.split('_',4)[3]):
return 1
elif float(x.split('_',4)[3]) < float(y.split('_',4)[3]):
return -1
else:
return 0
keys = self.__get_keys__(level)
keys.sort(tsort)
entries = self.__get_entries__(keys, self.channel)
entries = sorted(entries, key=lambda k: k['microtime'], reverse=True)
return entries
# ------------------------------------------------------------------------------------------ #
def __get_entries__(self, keys, channel):
entries = []
for key in keys:
entry = self.db.hget(key,channel)
entry = simplejson.dumps(entry.decode('utf-8'))
if not (entry is None):
try:
entry = entry.decode('utf-8').replace('None','"None"')
entry = re.sub("########[^]]*########", lambda x:x.group(0).replace('\"','').replace('\'',''),entry.replace("\\\"","########").replace("\\'","++++++++").replace("'",'"').replace('u"','"').replace('"{','{').replace('}"','}')).replace("########","\"")
entry = simplejson.loads(entry)
entry['key'] = key
entries.append(entry)
except:
pass
return entries
# ------------------------------------------------------------------------------------------ #
def publish(self, channel, message):
subscriber_count = self.db.execute_command('PUBSUB', 'NUMSUB', channel)
if channel.lower().find("reply") < 0 and subscriber_count[1] == 0:
raise Exception("No subscriber! Is Aura daemon running?")
self.db.publish(channel, message)
__author__ = 'gg'
# -*- coding: utf-8 -*-
from sqlalchemy import Boolean, Column, Date, DateTime, Float, Integer, String, Text, Time, ForeignKey
from libraries.database.database import db
from enum import Enum
class TerminalColors(Enum):
HEADER = '\033[95m'
OK_GREEN = "\033[32m"
OK_ORANGE = '\033[33m'
OK_BLUE = '\033[34m'
OK_PINK = '\033[35m'
WARNING = '\033[31m'
FAIL = '\033[41m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
ENDC = "\033[0m"
\ No newline at end of file
from enum import Enum
class Channels(Enum):
STANDARD = "aura"
# MAIN = "main"
IP_REPLY = "init_player_reply"
FNP_REPLY = "fetch_new_programme_reply"
GAP_REPLY = "get_act_programme_reply"
PMQ_REPLY = "print_message_queue_reply"
MPE_REPLY = "move_playlist_entry_reply"
DPE_REPLY = "delete_playlist_entry_reply"
IPE_REPLY = "insert_playlist_entry_reply"
\ No newline at end of file
class NoProgrammeLoadedException(Exception):
pass
class LQConnectionError(Exception):
value = None
message = ""
def __init__(self, value, message=""):
self.value = value
self.message = message
def __str__(self):
return repr(self.value)
class RedisConnectionException(Exception):
pass
class FallbackException(Exception):
pass
import simplejson
class ExceptionLogger:
# ------------------------------------------------------------------------------------------ #
def __get_error__(self, job, errornumber, data):
"""
Privat: Ermittelt Fehlermeldung, Job-Name (Klassenmethode) und Fehlercode für den Job aus error/controller_error.js
@type errornumber: string
@param errornumber: Die interne Fehlernummer der aufrufenden Methode
"""
### weil es eine "bound method" ist, kommmt data als string an!???
if data == None:
data = {}
if type(data) == type(str()):
data = simplejson.loads(data)
hasData = isinstance(data, (dict)) and len(data) > 0
if job in self.errorData:
errMsg = self.errorData[job][errornumber]
errID = self.errorData[job]['id'] + str(errornumber)
if hasData:
for key in data.keys():
errMsg = errMsg.replace('::' + key + '::', str(data[key]))
data['message'] = errMsg
data['job'] = job
data['code'] = errID
return data
# ------------------------------------------------------------------------------------------ #
def success(self, job, data=None, errnum='00', value='', section='execjob'):
"""
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.__get_error__(job, errnum, data)
self.job_result = {'message': error['message'], 'code': error['code'], 'success': 'success',
'job': error['job'], 'value': value, 'section': section}
self.redismessenger.send(error['message'], error['code'], 'success', error['job'], value, section)
# ------------------------------------------------------------------------------------------ #
def info(self, job, data=None, errnum='01', value='', section='execjob'):
"""
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.__get_error__(job, errnum, data)
self.job_result = {'message': error['message'], 'code': error['code'], 'success': 'info', 'job': error['job'],
'value': value, 'section': section}
self.redismessenger.send(error['message'], error['code'], 'info', error['job'], value, section)
# ------------------------------------------------------------------------------------------ #
def warning(self, job, data=None, errnum='01', value='', section='execjob'):
"""
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.__get_error__(job, errnum, data)
self.job_result = {'message': error['message'], 'code': error['code'], 'success': 'warning',
'job': error['job'], 'value': value, 'section': section}
self.redismessenger.send(error['message'], error['code'], 'warning', error['job'], value, section)
# ------------------------------------------------------------------------------------------ #
def error(self, job, data=None, errnum='01', value='', section='execjob'):
"""
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.__get_error__(job, errnum, data)
self.job_result = {'message': error['message'], 'code': error['code'], 'success': 'error', 'job': error['job'],
'value': value, 'section': section}
self.redismessenger.send(error['message'], error['code'], 'error', error['job'], value, section)
# ------------------------------------------------------------------------------------------ #
def fatal(self, job, data=None, errnum='01', value='', section='execjob'):
"""
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.__get_error__(job, errnum, data)
self.job_result = {'message': error['message'], 'code': error['code'], 'success': 'fatal', 'job': error['job'],
'value': value, 'section': section}
self.redismessenger.send(error['message'], error['code'], 'fatal', error['job'], value, section)
# ------------------------------------------------------------------------------------------ #
def __check_result__(self, result):
"""
Fehlerbehandlung
@type result: string
@param result: Ein Json-String
"""
try:
self.lq_error = simplejson.loads(result)
except:
return False
try:
if self.lq_error['success'] == 'success':
return True
else:
return False
except:
return False
\ No newline at end of file
__author__ = 'michel'
import os
class AuraMailer():
def __init__(self, adminMails, fromMail):
self.adminMails = adminMails
self.fromMail = fromMail
def sendAdminMail(self, subject, body):
adminMails = self.adminMails.split()
for mailTo in adminMails:
self.send(self.fromMail, mailTo, subject, body)
def send(self, mailFrom, mailTo, subject, body):
sendmail_location = "/usr/sbin/sendmail"
p = os.popen("%s -t" % sendmail_location, "w")
p.write("From: %s\n" % mailFrom)
p.write("To: %s\n" % mailTo)
p.write("Subject: " + subject + "\n")
p.write("\n") # blank line separating headers from body
p.write(body)
status = p.close()
return status
\ No newline at end of file
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# user.py
#
"""
Aura User Class - Benutzerverwaltung
"""
import os
import sys
import redis
import random
import string
## current directory
scriptdir = os.path.dirname(os.path.abspath(__file__))
# Hier stecken unsere eigenen Python libraries
package_dir = os.path.join(scriptdir, 'python')
path = list(sys.path)
# Das package_dir zu den Systempfaden hinzufügen, damit wir Importe durchführen können
sys.path.insert(0, package_dir)
"""
User verwalten
"""
class AuraUser(object):
def __init__(self):
self.db = redis.Redis()
self.dbname = 'aurausers'
self.userprefix = 'aurauser:'
pass
# ------------------------------------------------------------------------------------------ #
def delete(self, username):
"""
Delete an user
:param username:
:return: boolean
"""
userid = self.db.hget(self.dbname,username).decode("utf-8")
if not userid:
return False
else:
self.db.delete(self.userprefix + userid)
self.db.hdel(self.dbname,username)
return True
# ------------------------------------------------------------------------------------------ #
def setPassword(self,username, password):
"""
Set users password
:param username: string
:param password: string
:return: boolean
"""
userid = self.db.hget(self.dbname,username).decode("utf-8")
if not userid:
return False
self.db.hset(self.userprefix + userid, 'password', password)
return True
# ------------------------------------------------------------------------------------------ #
def hasRole(self, username, role):
"""
Compare users role
:param username: string
:param role: string
:return:boolean
"""
userid = self.db.hget(self.dbname,username).decode("utf-8")
dbrole = self.db.hget(self.userprefix + str(userid), 'role').decode("utf-8")
if(dbrole == "admin"):
return True
print("username: " + username + " - userid: " + userid + " - role: " + role + " - dbrole: " + dbrole)
return (dbrole == role)
# ------------------------------------------------------------------------------------------ #
def hasPassword(self,username, password):
"""
Compare users password with the given one
:param username: string
:param password: string
:return:
"""
userid = self.db.hget(self.dbname,username).decode("utf-8")
dbpassword = self.db.hget(self.userprefix + userid, 'password').decode("utf-8")
print("username: "+username+" - userid: "+userid+" - password: "+password+" - dbpassword: "+dbpassword)
print(dbpassword == password)
return (dbpassword == password)
# ------------------------------------------------------------------------------------------ #
def hasAdminRights(self, username, password):
"""
Check admin rights
:param username: username
:param password: password
:return:
"""
return (self.hasPassword(username,password) and self.hasRole(username, 'admin'))
# ------------------------------------------------------------------------------------------ #
def insertUser(self, username, password, role="user"):
"""
Insert or update user
:param username: string
:param password: string
:param role: string
:return: string - the password
"""
userid = self.db.hget(self.dbname,username).decode("utf-8")
if not userid:
userid = self.db.incr("next_aurauser_id")
self.db.hset(self.dbname,username,userid)
self.db.hmset(self.userprefix + userid, {"username" : username,"password" :password, "role" : role})
return password
# ------------------------------------------------------------------------------------------ #
def getUser(self, username):
"""
Get users data
:param username: string
:return: dict - userdata
"""
userid = self.db.hget(self.dbname,username).decode("utf-8")
return self.db.hgetall(self.userprefix + userid)
# ------------------------------------------------------------------------------------------ #
def getUserlist(self):
"""
get all users
:return: list - the userlist
"""
accounts=[]
keys = self.db.keys(self.userprefix + "*")
for key in keys:
accounts.append(self.db.hgetall(key))
return accounts
# ------------------------------------------------------------------------------------------ #
def getLogins(self):
"""
get usernames passwords as dict in format {username1:password1, username2;password2, ...}
:return:
"""
accounts={}
keys = self.db.keys(self.userprefix + "*")
for key in keys:
account = self.db.hgetall(key)
try:
accounts[account['username']] = account['password']
except:
pass
return accounts
# ------------------------------------------------------------------------------------------ #
def createPassword(self):
"""
create a new passoword
:return: string - the password
"""
password = ''.join(random.sample(string.lowercase+string.uppercase+string.digits,14))
return password
\ No newline at end of file
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# aurawhitelist
#
"""
Aura Whitelist - IP-Adressen oder Domains aus der Whitelist holen
"""
import os
import sys
import redis
"""
Whitelisting ips or hostnames
"""
class AuraWhitelist(object):
def __init__(self):
self.db = redis.Redis()
self.dbname = 'aurawhitelist'
pass
# ------------------------------------------------------------------------------------------ #
def getList(self):
"""
get the whitelist
:return: list - list of whitelisted ip's
"""
return self.db.lrange(self.dbname, 0, -1)
# ------------------------------------------------------------------------------------------ #
def add(self,address):
"""
Add ip/host to whitelist
:param address: string - ip or hostname
:return: boolean
"""
list = self.getList()
for item in list:
if item == address:
return False
self.db.lpush(self.dbname, address)
return True
# ------------------------------------------------------------------------------------------ #
def remove(self,address):
"""
Remove an ip or host from whitelist
:param address: string - ip or hostname
:return: boolean
"""
if not address:
return False
self.db.lrem(self.dbname, address, 1)
return True
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import sys
import time
import socket
import urllib.parse
import configparser
from io import StringIO
from libraries.exceptions.auraexceptions import LQConnectionError
""" LiquidSoapClient Class
Repräsentiert alle Kommandos, die Soundserver und Recorder kennen
"""
class LiquidSoapClient:
debug = False
socket_path = ""
def __init__(self, config, socket_filename):
"""
Constructor
@type socket_path: string
@param socket_path: Der Pfad zum Socket des Liquidsoap-Scripts
"""
self.socket_path = config.get('socketdir') + '/' + socket_filename
self.debug = config.get("debug")
if self.debug > 2:
print("socketpath: " + self.socket_path)
# init
self.connected = False
self.can_connect = True
self.message = ''
self.client = None
if sys.version_info <= (3, 2):
self.metareader = configparser.ConfigParser({'strict': False, 'interpolation': None})
else:
self.metareader = configparser.ConfigParser()
# ------------------------------------------------------------------------------------------ #
def connect(self):
"""
Verbindung herstellen
"""
try:
self.client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.client.connect(self.socket_path)
except socket.error as e:
self.connected = False
print(self.socket_path)
raise LQConnectionError(e, self.socket_path)
return False
else:
self.can_connect = True
self.connected = True
return True
# ------------------------------------------------------------------------------------------ #
def is_connected(self):
return self.connected
# ------------------------------------------------------------------------------------------ #
def write(self, data):
"""
Auf den Socket schreiben
@type data: string
@param data: Der String der gesendet wird
"""
if self.connected:
self.client.sendall(data.decode("UTF-8"))
# ------------------------------------------------------------------------------------------ #
def read_all(self, timeout=2):
"""
Vom Socket lesen, bis dieser "END" sendet
@type timeout: int
@param timeout: Ein optionales Timeout
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
# make socket non blocking
# self.client.setblocking(0)
data = '';
self.client.settimeout(3)
# recv something
# while True:
try:
time.sleep(0.1)
raw = self.client.recv(8192)
data = raw.decode()
except Exception as e:
print(e)
pass
return data
# ------------------------------------------------------------------------------------------ #
def read(self):
"""
Vom Socket lesen und anschließend quit senden, um den Server zu veranlassen, die Verbindung schließen
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
if self.connected:
ret = self.read_all()
ret = ret.splitlines()
try:
# print("pop from empty list?")
# print(ret)
ret.pop() # pop 'END' out
# print(ret)
# print("yes")
self.message = str.join('\n', ret)
# self.message = str(b'\n'.join(ret))
except Exception as e:
print(e)
# self.client.sendall('quit\n')
return self.message
# ------------------------------------------------------------------------------------------ #
def close(self):
"""
Quit senden und Verbindung schließen
"""
if self.connected:
message = "quit\r"
self.client.sendall(message.decode("UTF-8"))
self.client.close()
self.connected = False
# ------------------------------------------------------------------------------------------ #
def command(self, namespace, command, param=""):
"""
Kommando an Liquidosap senden
@type command: string
@param command: Kommando
@type namespace: string
@param namespace: Namespace/Kanal der angesprochen wird
@type param: mixed
@param param: ein optionaler Parameter
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
param = (param.strip() if param.strip() == "" else " " + urllib.parse.unquote(param.strip()))
if self.connected:
# print namespace + '.' + command + param + "\n"
if namespace is "":
message = str(command) + str(param) + str('\n')
else:
message = str(namespace) + str('.') + str(command) + str(param) + str('\n')
try:
if self.debug > 2:
print("LiquidSoapClient sending to LiquidSoap Server: "+message[0:len(message)-1])
# send all the stuff over the socket to liquidsoap server
self.client.sendall(message.encode())
if self.debug > 2:
print("LiquidSoapClient waiting for reply from LiquidSoap Server")
# wait for reply
self.read()
if self.debug > 2:
print("LiquidSoapClient got reply")
# self.client.close()
except:
print("Unexpected error:", sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])
raise
return self.message
else:
if self.debug:
print("LiquidSoapClient not connected to LiquidSoap Server")
return False
# ------------------------------------------------------------------------------------------ #
def simplecommand(self, command):
"""
Parameterloses Kommando ohne Namespace senden
@type command: string
@param command: Kommando
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
if self.connected:
message = str(command) + '\n'
self.client.sendall(message.decode("UTF-8"))
self.read()
# self.client.close()
return self.message
# ------------------------------------------------------------------------------------------ #
def getMetadata(self, rid):
"""
Parameterloses Kommando ohne Namespace senden
@type rid: string/int
@param rid: Die ID eines Requests
@rtype: dict
@return: Die Metadaten als dict
"""
meta = self.command('metadata ' + str(rid), 'request')
meta = '[root]\n' + meta
if sys.version_info <= (3, 2):
meta = StringIO.StringIO(meta)
try:
self.metareader.readfp(meta)
except configparser.ParsingError:
return False
else:
try:
self.metareader.read_string(meta)
except configparser.ParsingError:
return False
return self.metareader
# ------------------------------------------------------------------------------------------ #
def help(self):
"""
get liquidsoap server help
@rtype: string
@return: the response of the liquidsoap server
"""
if self.connected:
self.command('help', '')
print('read (' + str(len(self.message)) + ' bytes): ' + self.message)
return self.message
# ------------------------------------------------------------------------------------------ #
def version(self):
"""
Liquidsoap get version
@rtype: string
@return: the response of the liquidsoap server
"""
if self.connected:
message = 'version'
self.command(message, '')
return self.message
# ------------------------------------------------------------------------------------------ #
def uptime(self):
"""
Liquidsoap get uptime
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
if self.connected:
self.command('uptime', '')
print('read (' + str(len(self.message)) + ' bytes): ' + self.message)
return self.message
# ------------------------------------------------------------------------------------------ #
def byebye(self):
"""
Liquidsoap say byebye
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
if self.connected:
message = 'quit'
self.command("", message) # client.sendall(message.encode())
# self.read()
# print("read: " + self.message)
return self.message
\ No newline at end of file
import os
import codecs
import urllib
import tempfile
import simplejson
import traceback
import datetime
from libraries.base.parsexml import parsexml
from modules.communication.liquidsoap.playerclient import LiquidSoapPlayerClient
from modules.communication.liquidsoap.recorderclient import LiquidSoapRecorderClient
from modules.communication.liquidsoap.initthread import LiquidSoapInitThread
from libraries.enum.consolecolor import TerminalColors
from libraries.exceptions.auraexceptions import LQConnectionError
from libraries.database.broadcasts import TrackService
from libraries.exceptions.exception_logger import ExceptionLogger
class LiquidSoapCommunicator(ExceptionLogger):
lqc = None
lqcr = None
debug = False
transaction = 0
# controller = None
channels = None
scheduler = None
# ------------------------------------------------------------------------------------------ #
def __init__(self, config):
"""
Constructor
@type lqs_socket: string
@param lqs_socket: Liquidsoap Player Socket
@type lqs_recsocket: string
@param lqs_recsocket: Liquidsoap Recorder Socket
"""
self.debug = config.get("debug")
self.lqc = LiquidSoapPlayerClient(config, "simplestmixer.sock")
self.lqcr = LiquidSoapRecorderClient(config, "record.sock")
#try:
# self.get_all_channels()
#except LQConnectionError as lqe:
# print(lqe)
#def set_controller(self, controller):
# self.controller = controller
# ------------------------------------------------------------------------------------------ #
def set_volume(self, mixernumber, volume):
print("LiquidSoapCommunicator.set_volume")
return self.__send_lqc_command__(self.lqc, "mixer", "volume", mixernumber, volume)
# ------------------------------------------------------------------------------------------ #
def get_active_mixer(self):
"""
get active mixer in liquidsoap server
:return:
"""
activeinputs = []
# enable more control over the connection
self.enable_transaction(True)
inputs = self.get_all_channels()
cnt = 0
for input in inputs:
status = self.__get_mixer_status__(cnt)
if "selected=true" in status:
activeinputs.append(input)
cnt = cnt + 1
self.disable_transaction(True)
return activeinputs
# ------------------------------------------------------------------------------------------ #
def get_active_channel(self):
"""
gets active channel from programme
:return:
"""
active_source = self.scheduler.get_active_source()
if active_source == "":
return ""
return self.channels.index(active_source)
# ------------------------------------------------------------------------------------------ #
def get_mixer_status(self):
inputstate = {}
self.enable_transaction()
self.open_conn(self.lqc)
inputs = self.get_all_channels()
cnt = 0
for input in inputs:
inputstate[input] = self.__get_mixer_status__(cnt)
cnt = cnt + 1
self.disable_transaction()
self.close_conn(self.lqc)
return inputstate
# ------------------------------------------------------------------------------------------ #
def activate(self, entry, input_type):
if self.scheduler.get_active_source() == input_type:
self.insert_track_service_entry(entry)
print(TerminalColors.OK_PINK.value + "LiquidSoapCommunicator " + input_type + " already active! Time: " + str(datetime.datetime.now()) + TerminalColors.ENDC.value)
else:
print(TerminalColors.OK_PINK.value + "LiquidSoapCommunicator is activating " + input_type + "! Time: " + str(datetime.datetime.now()) + TerminalColors.ENDC.value)
self.enable_transaction(True)
others = self.all_inputs_but(input_type)
for o in others:
self.channel_volume(o, 0)
self.channel_volume(input_type, entry.volume)
self.disable_transaction(True)
self.insert_track_service_entry(entry)
self.playlist_insert()
# ------------------------------------------------------------------------------------------ #
def insert_track_service_entry(self, schedule_entry):
trackservice_entry = TrackService()
trackservice_entry.playlist_id = schedule_entry.playlist_id
trackservice_entry.entry_num = schedule_entry.entry_num
trackservice_entry.source = schedule_entry.source
trackservice_entry.store(add=True, commit=True)
# ------------------------------------------------------------------------------------------ #
def all_inputs_but(self, input_type):
activemixer_copy = self.get_all_channels().copy() # get_active_mixer().copy()
print("holding ", activemixer_copy)
try:
activemixer_copy.remove(input_type)
except ValueError:
# Value not in list
return activemixer_copy
return activemixer_copy
# ------------------------------------------------------------------------------------------ #
def get_all_channels(self):
if self.channels is None or self.channels == ['']:
self.channels = self.__send_lqc_command__(self.lqc, "mixer", "inputs")
return self.channels
# ------------------------------------------------------------------------------------------ #
def reload_channels(self):
self.channels = None
return self.get_all_channels()
# ------------------------------------------------------------------------------------------ #
def __get_mixer_status__(self, mixernumber):
return self.__send_lqc_command__(self.lqc, "mixer", "status", mixernumber)
# ------------------------------------------------------------------------------------------ #
def init_player(self):
LiquidSoapInitThread(self, self.lqc).start()
return "Started LiquidSoapInitThread!"
# ------------------------------------------------------------------------------------------ #
def get_client(self):
return self.lqc
# ------------------------------------------------------------------------------------------ #
def all_data(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.__send_lqc_command__(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.__send_lqc_command__(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.__send_lqc_command__(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.__send_lqc_command__(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.__send_lqc_command__(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.__send_lqc_command__(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.__send_lqc_command__(self.lqc, 'listChannels', False)
index = channels.index(channel)
message = self.__send_lqc_command__(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.__send_lqc_command__(self.lqc, 'listChannels', False)
index = channels.index(channel)
# a activate channel
message = self.__send_lqc_command__(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.__send_lqc_command__(self.lqc, 'get_queue', channel)
rids = message.strip().split(' ')
data['tracks'] = []
for rid in rids:
if rid != '':
# get each rids metadata
metadata = self.__send_lqc_command__(self.lqc, 'getMetadata', rid)
track = self._metadata_format(metadata)
if not 'title' in track:
if 'location' in track:
track['title'] = os.path.basename(track['location'])
elif 'filename' in track:
track['title'] = os.path.basename(track['filename'])
else:
track['title'] = 'unknown'
data['tracks'].extend([track])
channels = self.__send_lqc_command__(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.__send_lqc_command__(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.__send_lqc_command__(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.__send_lqc_command__(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.__send_lqc_command__(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.__send_lqc_command__(self.lqc, 'listChannels')
foundChannel = ''
if not isinstance(channels, list):
self.error('02')
else:
for index, item in enumerate(channels):
if item == channel:
foundChannel = self.__send_lqc_command__(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_activate(self, channel, activate):
channels = self.get_all_channels()
try:
index = channels.index(channel)
if len(channel) < 1:
self.warning('02')
except:
self.error('03')
else:
message = self.__send_lqc_command__(self.lqc, "mixer", "select", index, activate)
return message
# ------------------------------------------------------------------------------------------ #
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
"""
# Liquidsoap Kommando
#channels = self.__sendLqcCommand__(self.lqc, 'listChannels', False)
channels = self.get_all_channels()
try:
index = channels.index(channel)
if len(channel) < 1:
self.warning('02')
except:
self.error('03')
else:
message = self.__send_lqc_command__(self.lqc, 'mixer', 'volume', str(index), str(int(volume)))
return message
# if message.find('volume=' + str(volume) + '%'):
# self.controller.success('01', str(volume))
# else:
# self.controller.warning('01')
# ------------------------------------------------------------------------------------------ #
def current_data(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.__send_lqc_command__(self.lqc, 'currentTrack')
rid = message.strip()
metadata = self.__send_lqc_command__(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.__send_lqc_command__(self.lqc, 'listChannels', False)
index = channels.index(channel)
state_data = {}
try:
index = channels.index(channel)
status = self.__send_lqc_command__(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 liquidsoap_help(self):
data = self.__send_lqc_command__(self.lqc, 'help')
if not data:
self.warning('01')
else:
self.success('00', data)
self.notifyClient()
# ------------------------------------------------------------------------------------------ #
def list_channels(self):
"""
Channels auflisten (Simple JSON)
"""
# Liquidsoap Kommando
channels = self.__send_lqc_command__(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.__send_lqc_command__(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.__send_lqc_command__(self.lqc, 'flush')
self.success('00')
self.notifyClient()
# ------------------------------------------------------------------------------------------ #
def playlist_insert(self, uri, pos):
"""
Track in die Playlist einfuegen
"""
data = self.__send_lqc_command__(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 = urllib.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.__send_lqc_command__(self.lqc, 'flush')
data = self.__send_lqc_command__(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.fire_event('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.__send_lqc_command__(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.__send_lqc_command__(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.__send_lqc_command__(self.lqc, 'deactivate', '0')
data = self.__send_lqc_command__(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.__send_lqc_command__(self.lqc, 'activate', '0')
if when == 'now':
# immediately skip all playing channels
# and activate the playlist channel
channels = self.__send_lqc_command__(self.lqc, 'listChannels')
if not isinstance(channels, list):
self.error('03')
elif len(channels) < 1:
self.warning('02')
else:
# xrange
for i in range(len(channels)):
status = self.__send_lqc_command__(self.lqc, 'status', 'mixer', str(i + 1))
if "selected=true" in status:
status = self.__send_lqc_command__(self.lqc, 'deactivate', str(i + 1))
status = self.__send_lqc_command__(self.lqc, 'skip', 'mixer', str(i + 1))
self.__send_lqc_command__(self.lqc, 'activate', '0')
# send the play command
data = self.__send_lqc_command__(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.__send_lqc_command__(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.__send_lqc_command__(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.__send_lqc_command__(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.__send_lqc_command__(self.lqc, 'skip')
self.success('00')
self.notifyClient()
# ------------------------------------------------------------------------------------------ #
def version(self):
"""
get version
"""
data = self.__send_lqc_command__(self.lqc, 'version')
self.success('00', data)
self.notifyClient()
# ------------------------------------------------------------------------------------------ #
def uptime(self):
"""
get uptime
"""
data = self.__send_lqc_command__(self.lqc, 'uptime')
self.success('00', data)
self.notifyClient()
# ------------------------------------------------------------------------------------------ #
def on_air(self):
"""
get whats playing now
"""
data = self.__send_lqc_command__(self.lqc, 'on_air')
self.success('00', data)
self.notifyClient()
# ------------------------------------------------------------------------------------------ #
def recorder_start(self):
"""
Recorder starten
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
message = self.__send_lqc_command__(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.__send_lqc_command__(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.__send_lqc_command__(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 __send_lqc_command__(self, lqs_instance, namespace, command, *args):
"""
Ein Kommando an Liquidsoap senden
@type lqs_instance: object
@param lqs_instance: Instance of LiquidSoap Client
@type namespace: string
@param namespace: Namespace of function
@type command: string
@param command: Function name
@type args: list
@param args: List of parameters
@rtype: string
@return: Response from LiquidSoap
"""
try:
# connect
if not self.transaction:
self.open_conn(lqs_instance)
if self.debug:
print("LiquidSoapCommunicator is calling " + str(namespace) + "." + str(command) + str(args))
# call wanted function ...
func = getattr(lqs_instance, namespace)
# ... and fetch the result
result = func(command, *args)
if self.debug:
if len(args) == 0:
print("LiquidSoapCommunicator got response " + str(result))
else:
print("LiquidSoapCommunicator got response " + str(result))
# say byebye
if not self.transaction:
self.close_conn(lqs_instance)
except Exception as e:
# Verbindung gescheitert - Fehler an Client
# if command.find('record') > -1:
# raise e
# self.fatal('02')
# else:
# raise e
# self.fatal('01')
traceback.print_exc()
raise e
# Instanz/Thread zerstören - aufrufende Funktion wird nicht weiter abgearbeitet
#del self
else:
return result
# ------------------------------------------------------------------------------------------ #
def enable_transaction(self, connect=False):
self.transaction = self.transaction + 1
if self.debug:
print(TerminalColors.WARNING.value + "ENabling transaction! cnt: " + str(self.transaction) + TerminalColors.ENDC.value)
if self.transaction > 1:
return
if connect:
self.open_conn()
# ------------------------------------------------------------------------------------------ #
def disable_transaction(self, disconnect=False):
self.transaction = self.transaction - 1
if self.debug:
print(TerminalColors.WARNING.value + "DISabling transaction! cnt: " + str(self.transaction) + TerminalColors.ENDC.value)
if self.transaction > 0:
return
if disconnect:
self.close_conn()
self.transaction = False
# ------------------------------------------------------------------------------------------ #
def open_conn(self, socket=None):
if socket is None:
socket = self.lqc
if self.transaction > 1:
return # already connected
if self.debug:
print(TerminalColors.OK_GREEN.value + "LiquidSoapCommunicator opening conn" + TerminalColors.ENDC.value)
socket.connect()
# ------------------------------------------------------------------------------------------ #
def close_conn(self, socket=None):
if socket is None:
socket = self.lqc
if self.transaction > 0:
return # still need conn
socket.byebye()
if self.debug:
print(TerminalColors.OK_BLUE.value + "LiquidSoapCommunicator closed conn" + TerminalColors.ENDC.value)
import time
import threading
import traceback
from libraries.exceptions.auraexceptions import LQConnectionError
class LiquidSoapInitThread(threading.Thread):
socket = None
liquidsoapcommunicator = None
def __init__(self, liquidsoapcommunicator, socket):
threading.Thread.__init__(self)
self.socket = socket
self.liquidsoapcommunicator = liquidsoapcommunicator
def run(self):
try:
# sleep needed, because the socket is created to slow by liquidsoap
time.sleep(5)
self.liquidsoapcommunicator.enable_transaction(True)
# reset channels and reload them
self.liquidsoapcommunicator.channels = None
channels = self.liquidsoapcommunicator.get_all_channels()
# set every volume to 0
cnt = 0
for c in channels:
#self.liquidsoapcommunicator.__send_lqc_command__(self.socket, "mixer", "volume", cnt, "0")
self.liquidsoapcommunicator.channel_volume(c, "0")
cnt = cnt + 1
# select all channels
cnt = 0
for c in channels:
self.liquidsoapcommunicator.channel_activate(c, True)
#self.liquidsoapcommunicator.__send_lqc_command__(self.socket, "mixer", "select", cnt, "true")
cnt = cnt + 1
# thats hacky => the cracklings stop after 5 seconds after restarting the liquidsoap server
time.sleep(3)
activechannel = self.liquidsoapcommunicator.get_active_channel()
if self.liquidsoapcommunicator.debug > 2:
print("LiquidSoapInitThread sets activechannel: "+str(activechannel))
if activechannel != "":
self.liquidsoapcommunicator.__send_lqc_command__(self.socket, "mixer", "volume", activechannel, 100)
# self.liquidsoapcommunicator.close_conn(self.socket)
self.liquidsoapcommunicator.disable_transaction(True)
except LQConnectionError as e:
print("Liquidsoap connection ERROR! Restart LQ Server!")
print(e.message)
print(e)
except Exception as e:
traceback.print_exc()
print(e)
from modules.communication.liquidsoap.client import LiquidSoapClient
class LiquidSoapPlayerClient(LiquidSoapClient):
# ------------------------------------------------------------------------------------------ #
def mixer(self, command, *args):
if command == "status":
return self.mixerstatus(*args)
if command == "inputs":
return self.mixerinputs()
if command == "volume":
return self.mixervolume(*args)
if command == "select":
if len(args) == 2:
return self.mixerselect(args[0], args[1])
return "LiquidSoapPlayerClient does not understand "+command+str(args)
# ------------------------------------------------------------------------------------------ #
def mixerinputs(self):
"""
List all channels on the mixer
@type namespace: string
@param namespace: lqs namespace
@rtype: list
@return: answer of our lqs server
"""
# self.logger.info("listchannels modules/controller/liquidsoap.py")
# send command
self.command("mixer", "inputs")
# convert to list and return it
return self.message.strip().split(' ')
# ------------------------------------------------------------------------------------------ #
def mixerstatus(self, pos=""):
"""
Get state of a source in the mixer
@type pos: string
@param pos: Mixerposition
@rtype: string
@return: Response from LiquidSoap
"""
self.command("mixer", "status", str(pos))
return self.message
# ------------------------------------------------------------------------------------------ #
def mixerselect(self, pos, activate):
"""
Kanal/Source aktivieren
@type pos: string
@param pos: Die Position
@type namespace: string
@param namespace: Namespace der Source
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
self.command("mixer", "select", str(pos) + " " + str(activate).lower())
return self.message
# ------------------------------------------------------------------------------------------ #
def mixervolume(self, pos, volume):
"""
set channel volume
:param pos:
:param volume:
:return:
"""
self.command("mixer", "volume", str(pos) + " " + str(volume))
return self.message
# ------------------------------------------------------------------------------------------ #
def skip(self, namespace="playlist", pos=""):
"""
Source skippen
@type namespace: string
@param namespace: Namespace der Source
@type pos: string
@param pos: Die Position - optional - Position des Channels vom Mixer benötigt
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
self.command('skip', namespace, pos)
return self.message
# ------------------------------------------------------------------------------------------ #
def remove(self, pos, namespace="playlist"):
"""
Track aus der secondary_queue oder der Playlist entfernen
@type pos: string
@param pos: Die Position
@type namespace: string
@param namespace: Namespace der Source
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
self.command('remove', namespace, str(pos))
return self.message
# ------------------------------------------------------------------------------------------ #
def insert(self, uri, pos='0', namespace="playlist"):
"""
Track einfügen
@type uri: string
@param uri: Uri einer Audiodatei
@type pos: string
@param pos: Die Position
@type namespace: string
@param namespace: Namespace der Source
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
self.command('insert', namespace, str(pos) + ' ' + uri)
return self.message
# ------------------------------------------------------------------------------------------ #
def move(self, fromPos, toPos, namespace="playlist"):
"""
Track von Position fromPos nach Position toPos verschieben
@type fromPos: string/int
@param fromPos: Position des zu verschiebenden Tracks
@type toPos: string
@param toPos: Die Position zu der verschoben werden soll
@type namespace: string
@param namespace: Namespace der Source
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
self.command('move', namespace, str(fromPos) + ' ' + str(toPos))
return self.message
# ------------------------------------------------------------------------------------------ #
def play(self, namespace="playlist"):
"""
Source abspielen - funktioniert nur bei Playlist
@type namespace: string
@param namespace: Namespace der Source
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
self.command('play', namespace)
return self.message
# ------------------------------------------------------------------------------------------ #
def pause(self, namespace="playlist"):
"""
Source pausieren/stoppen - funktioniert nur bei Playlist
@type namespace: string
@param namespace: Namespace der Source
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
self.command('pause', namespace)
return self.message
# ------------------------------------------------------------------------------------------ #
def flush(self, namespace="playlist"):
"""
Playlist leeren
@type namespace: string
@param namespace: Namespace der Source
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
self.command('flush', namespace)
return self.message
# ------------------------------------------------------------------------------------------ #
def push(self, uri, namespace="playlist"):
"""
Track einfügen und abspielen (wenn source aktiv ist)
@type uri: string
@param uri: Uri eines Audios
@type namespace: string
@param namespace: Namespace der Source
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
self.command('push', namespace, str(uri))
return self.message
# ------------------------------------------------------------------------------------------ #
def playlistData(self):
"""
Metadaten der Playlist ausgeben
@rtype: string
@return: Ein Json-String
"""
self.command('data', 'playlist')
return self.message
# ------------------------------------------------------------------------------------------ #
def seek(self, duration, namespace="playlist"):
"""
Aktuell laufenen Track des Kanals vorspulen
@type duration: string/int
@param duration: Dauer in Sekunden
@type namespace: string
@param namespace: Namespace der Source
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
self.command('seek', namespace, str(duration))
return self.message
# ------------------------------------------------------------------------------------------ #
def get_queue(self, namespace="ch1", queue='queue'):
"""
Queue eines Kanals ausgeben
@type namespace: string
@param namespace: Namespace der Source
@type queue: string
@param queue: Name des queues (queue, primary_queue, secondary_queue)
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
self.command(queue, namespace)
return self.message
# ------------------------------------------------------------------------------------------ #
def loadPlaylist(self, uri, params="", namespace="playlist"):
"""
Playlist laden
@type uri: string
@param uri: Uri einer Playlist im XSPF-Format
@type params: string
@param params: obsolete
@type namespace: string
@param namespace: Namespace der Source - hier nur playlist
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
self.command('load', namespace, uri + params)
return self.message
# ------------------------------------------------------------------------------------------ #
def currentTrack(self, namespace="request"):
"""
Das oder die ID(s) der gerade abgespielten requests erhalten
@type namespace: string
@param namespace: Namespace der Source
@rtype: string
@return: Die Antwort des Liquidsoap-Servers (als String)
"""
self.command('on_air', namespace)
return self.message
# ------------------------------------------------------------------------------------------ #
def volume(self, pos, volume, namespace="mixer"):
"""
Lautstärke eines Kanals setzen
@type pos: int/string
@param pos: Die Position/ Nummer des Kanals (playlist=0)
@type volume: int/string
@param volume: Zahl von 1 -100
@type namespace: string
@param namespace: Namespace der Source (immer mixer)
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
self.command('volume', namespace, str(pos) + ' ' + str(volume))
return self.message
# ------------------------------------------------------------------------------------------ #
def playlist_remaining(self):
"""
Wie lange läuft der aktuelle Track der Playlist noch
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
self.command('remaining', 'playlist')
return self.message
# ------------------------------------------------------------------------------------------ #
def list_channels(self):
"""
Channels auflisten (Simple JSON)
"""
# Liquidsoap Kommando
channels = self.sendLqcCommand(self.lqc, 'mixer', 'inputs')
if not isinstance(channels, list):
self.error('02')
elif len(channels) < 1:
self.warning('01')
else:
self.success('00', channels)
self.notifyClient()
\ No newline at end of file
from modules.communication.liquidsoap.client import LiquidSoapClient
class LiquidSoapRecorderClient(LiquidSoapClient):
# ------------------------------------------------------------------------------------------ #
def recorder_setfilename(self, filename):
"""
Dateinamen für Aufnahme (Vorproduktion) definieren
@type filename: string
@param filename: Dateiname - Angabe ohne Verzeichnis und mit Extension
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
self.command('setfilename', 'record', str(filename))
return self.message
# ------------------------------------------------------------------------------------------ #
def stop_record(self):
"""
Recorder stoppen
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
message = self.command('stop', 'record')
return self.message
# ------------------------------------------------------------------------------------------ #
def start_record(self):
"""
Recorder starten
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
self.command('start', 'record')
return self.message
# ------------------------------------------------------------------------------------------ #
def recorder_data(self):
"""
Daten des recorders erhalten
@rtype: string
@return: Die Antwort des Liquidsoap-Servers
"""
self.command('curfile', 'record')
return self.message
\ No newline at end of file
import threading
import sys
import redis
import random
import string
import time
from datetime import datetime
from threading import Event
from libraries.security.user import AuraUser
from libraries.database.statestore import RedisStateStore
from modules.communication.redis.messenger import RedisMessenger
from libraries.exceptions.auraexceptions import RedisConnectionException
from libraries.enum.consolecolor import TerminalColors
from libraries.enum.redischannels import Channels
class ServerRedisAdapter(threading.Thread, RedisMessenger):
debug = False
pubsub = None
config = None
redisdb = None
channel = ""
auracontroller = None
redisclient = None
def __init__(self, debug=False):
threading.Thread.__init__(self)
RedisMessenger.__init__(self)
self.debug = debug
# init
threading.Thread.__init__ (self)
self.shutdown_event = Event()
self.channel = Channels.STANDARD.value
self.section = ''
self.rstore = RedisStateStore()
self.errnr = '00'
self.components = {'controller':'01', 'scheduling':'02', 'playd':'03', 'recorder':'04', 'helpers':'09'}
self.fromMail = ''
self.adminMails = ''
self.redisclient = ClientRedisAdapter()
# ------------------------------------------------------------------------------------------ #
def set_controller(self, auracontroller):
self.auracontroller = auracontroller
self.scheduler = auracontroller.scheduler
# ------------------------------------------------------------------------------------------ #
def set_config(self, config):
self.config = config
# ------------------------------------------------------------------------------------------ #
def run(self):
self.redisdb = redis.Redis()
self.pubsub = self.redisdb.pubsub()
self.pubsub.subscribe(self.channel)
print(TerminalColors.OK_ORANGE.value + "waiting for REDIS message on channel " + self.channel + TerminalColors.ENDC.value)
for item in self.pubsub.listen():
if item["type"] == "subscribe":
continue
print(TerminalColors.OK_ORANGE.value + "received REDIS message: " + TerminalColors.ENDC.value + str(item) )
item["channel"] = self.decode_if_needed(item["channel"])
item["data"] = self.decode_if_needed(item["data"])
try:
self.work(item)
except RedisConnectionException as rce:
print(rce)
#traceback.print_exc()
print(TerminalColors.OK_ORANGE.value + "waiting for REDIS message on channel " + self.channel + TerminalColors.ENDC.value)
self.pubsub.unsubscribe()
print(self, "unsubscribed from " + self.channel + " and finished")
# ------------------------------------------------------------------------------------------ #
def decode_if_needed(self, val):
if isinstance(val, bytes):
return val.decode("utf-8")
return val
# ------------------------------------------------------------------------------------------ #
def listen_for_one_message(self, channel, socket_timeout=2):
self.redisdb = redis.Redis(socket_timeout=socket_timeout)
self.pubsub = self.redisdb.pubsub()
self.pubsub.subscribe(channel)
try:
if self.debug:
print("I am listening on channel '"+channel+"' for "+str(socket_timeout)+" seconds")
for item in self.pubsub.listen():
it = self.receive_message(item)
if it is not None:
break
except redis.exceptions.TimeoutError as te:
raise te
return item["data"]
# ------------------------------------------------------------------------------------------ #
def receive_message(self, item):
if item["type"] == "subscribe":
if self.debug > 2:
print("i am subscribed to channel " + item["channel"].decode("utf-8"))
return None
item["channel"] = item["channel"].decode("utf-8")
if isinstance(item["data"], bytes):
item["data"] = item["data"].decode("utf-8")
self.pubsub.unsubscribe()
return item
# ------------------------------------------------------------------------------------------ #
def work(self, item):
if item["data"] == "fetch_new_programme":
self.execute(Channels.FNP_REPLY.value, self.auracontroller.fetch_new_programme, )
elif item["data"] == "init_player":
self.execute(Channels.IP_REPLY.value, self.auracontroller.init_player, )
elif item["data"] == "get_act_programme":
self.execute(Channels.GAP_REPLY.value, self.auracontroller.get_act_programme, )
elif item["data"] == "print_message_queue":
self.execute(Channels.PMQ_REPLY.value, self.scheduler.print_message_queue, )
elif item["data"].find("swap_playlist_entries") >= 0:
extracted = item["data"].split()[1:3]
param = {"from_index": extracted[0], "to_index": extracted[1]}
self.execute(Channels.MPE_REPLY.value, self.scheduler.swap_playlist_entries, param)
elif item["data"].find("delete_playlist_entry") >= 0:
entrynum = item["data"].split()[1]
print("entry to del: ", entrynum)
self.execute(Channels.DPE_REPLY.value, self.scheduler.delete_playlist_entry, entrynum)
elif item["data"].find("insert_playlist_entry") >= 0:
extracted = item["data"].split()[1:3]
param = {"fromtime": extracted[0], "source": extracted[1]}
self.execute(Channels.IPE_REPLY.value, self.scheduler.insert_playlist_entry, param)
else:
raise RedisConnectionException("ServerRedisAdapter Cannot understand command: " + item["data"])
# ------------------------------------------------------------------------------------------ #
def execute(self, channel, f, param=None):
if param:
reply = f(param)
else:
reply = f()
if reply is None:
reply = ""
# sometimes the sender is faster than the receiver. redis messages would be lost
time.sleep(0.1)
print(TerminalColors.OK_ORANGE.value + "replying " + reply + " on channel " + channel + TerminalColors.ENDC.value)
# publish
self.redisclient.publish(channel, reply)
# ------------------------------------------------------------------------------------------ #
def join_comm(self):
try:
while self.is_alive():
print(str(datetime.now())+" joining")
self.join()
print("join out")
# if cnt % 30 == 0:
# print(datetime.datetime.now().isoformat())
# server.printLastMessages()
# cnt = 0
# cnt = cnt + 1
except (KeyboardInterrupt, SystemExit):
# Dem Server den Shutdown event setzen
# server.shutdown_event.set()
# Der Server wartet auf Eingabe
# Daher einen Client initiieren, der eine Nachricht schickt
self.halt()
sys.exit('Terminated')
# ------------------------------------------------------------------------------------------ #
def halt(self):
"""
Stop the server
"""
if self.shutdown_event.is_set():
return
try:
del self.auracontroller
except:
pass
self.shutdown_event.set()
result = 'failed'
try:
result = self.socket.unbind("tcp://"+self.ip+":"+self.port)
except:
pass
#self.socket.close()
# ------------------------------------------------------------------------------------------ #
def send(self, message):
"""
Send a message to the client
:param message: string
"""
if not self.can_send:
print("sending a "+str(len(message))+" long message via ZMQ.")
self.socket.send(message.encode("utf-8"))
self.can_send = False
else:
print("cannot send message via ZMQ: "+str(message))
# ------------------------------------------------------------------------------------------ #
@staticmethod
def get_accounts(self):
"""
Get accounts from redis db
:return: llist - a list of accounts
"""
accounts = AuraUser().getLogins()
db = redis.Redis()
internaccount = db.get('internAccess')
if not internaccount:
user = ''.join(random.sample(string.ascii_lowercase,10))
password = ''.join(random.sample(string.ascii_lowercase+string.ascii_uppercase+string.digits,22))
db.set('internAccess', user + ':' + password)
intern = [user, password]
else:
intern = internaccount.split(':')
accounts[intern[0]] = intern[1]
return accounts
class ClientRedisAdapter(RedisMessenger):
debug = False
def __init__(self, debug=True):
RedisMessenger.__init__(self)
self.debug = debug
# ------------------------------------------------------------------------------------------ #
def publish(self, channel, message):
if type(channel) == Channels:
channel = channel.value
self.rstore.publish(channel, message)
# -*- coding: utf-8 -*-
import time
import logging
import datetime
from libraries.database.statestore import RedisStateStore
from libraries.reporting.mail import AuraMailer
from libraries.exceptions.auraexceptions import FallbackException
from libraries.enum.redischannels import Channels
"""
Meldungen an den StateStore schicken
"""
class RedisMessenger():
rstore = None
def __init__(self):
"""
Constructor
"""
self.channel = Channels.STANDARD
self.section = ''
self.rstore = RedisStateStore()
self.errnr = '00'
self.components = {'controller':'01', 'scheduling':'02', 'playd':'03', 'recorder':'04', 'helpers':'09'}
self.fromMail = ''
self.adminMails = ''
# ------------------------------------------------------------------------------------------ #
def set_channel(self, channel):
"""
Einen "Kanal" setzen - zb scheduling
@type channel: string
@param channel: Kanal/Name der Komponente
"""
self.channel = channel
if channel in self.components:
self.errnr = self.components[channel]
self.rstore.set_channel(channel)
# ------------------------------------------------------------------------------------------ #
def set_section(self, section):
"""
Einen Sektion / Gültigkeitsbereich der Meldung setzen - zb internal
@type section: string
@param section: Gültigkeitsbereich
"""
self.section = section
# ------------------------------------------------------------------------------------------ #
def set_mail_addresses(self, fromMail, adminMails):
"""
Einen Sektion / Gültigkeitsbereich der Meldung setzen - zb internal
@type section: string
@param section: Gültigkeitsbereich
"""
self.fromMail = fromMail
self.adminMails = adminMails
# ------------------------------------------------------------------------------------------ #
def send(self, message, code, level, job, value='', section=''):
"""
Eine Message senden
@type message: string
@param message: menschenverständliche Nachricht
@type code: string
@param code: Fehlercode - endet mit 00 bei Erfolg
@type level: string
@param level: Error-Level - info, warning, error, fatal
@type job: string
@param job: Name der ausgeführten Funktion
@type value: string
@param value: Ein Wert
@type section: string
@param section: Globale Sektion überschreiben
"""
section = self.section if section == '' else section
self.time = str(datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S:%f'))
self.utime = time.time()
state = {'message':message.strip().replace("'","\\'"), 'code':self.errnr + str(code),'job':job,'value':value}
self.rstore.set_section(section)
self.rstore.store(level, state)
# TODO: hier kann auch was zu redis gepostet werden
if level == 'info' or level == 'success':
logging.info(message)
elif level == 'warning':
logging.warning(message)
elif level == 'error':
logging.error(message)
self.send_admin_mail(level, message, state)
elif level == 'fatal':
logging.critical(message)
self.send_admin_mail(level, message, state)
# ------------------------------------------------------------------------------------------ #
def say_alive(self):
"""
Soll alle 20 Sekunden von den Komponenten ausgeführt werden,
um zu melden, dass sie am Leben sind
"""
self.rstore.set_alive_state()
# ------------------------------------------------------------------------------------------ #
def get_alive_state(self, channel):
"""
Live State abfragen
@type channel: string
@param channel: Channel/Komponente
"""
return self.rstore.get_alive_state(channel)
# ------------------------------------------------------------------------------------------ #
def set_state(self, name, value, expires=None, channel=None):
"""
Kündigt einen Event an
@type name: string
@param name: Name des state
@type value: string
@param value: Wert
@type channel: string
@param channel: Kanal (optional)
"""
if not channel:
channel = self.channel
self.rstore.set_state(name, value, expires, channel)
# ------------------------------------------------------------------------------------------ #
def queue_add_event(self, name, eventtime, value, channel=None):
"""
Kündigt einen Event an
@type name: string
@param name: der Name des Events
@type eventtime: string|datetime.datetime
@param eventtime: Datum und Zeit des events
@type value: dict
@param value: Werte
@type channel: string
@param channel: Kanal (optional)
"""
if not channel:
channel = self.channel
if type(eventtime) == type(str()):
eventtime_str = datetime.datetime.strptime(eventtime[0:16].replace(' ','T'), "%Y-%m-%dT%H:%M").strftime("%Y-%m-%dT%H:%M")
elif type(eventtime) is datetime.datetime:
eventtime_str = eventtime.strftime("%Y-%m-%dT%H:%M")
else:
raise TypeError('eventtime must be a datetime.date or a string, not a %s' % type(eventtime))
self.rstore.queue_add_event(eventtime_str, name, value, channel)
# ------------------------------------------------------------------------------------------ #
def queue_remove_events(self, name, channel=None):
"""
Löscht Events
@type name: string
@param name: der Name des Events
@type channel: string
@param channel: Kanal (optional)
"""
if not channel:
channel = self.channel
self.rstore.queue_remove_events(name, channel)
# ------------------------------------------------------------------------------------------ #
def fire_event(self, name, value, channel=None):
"""
Feuert einen Event
@type name: string
@param name: der Name des Events
@type value: dict
@param value: Werte
@type channel: string
@param channel: Kanal (optional)
"""
if not channel:
channel = self.channel
self.rstore.fire_event(name, value, channel)
# ------------------------------------------------------------------------------------------ #
def get_event_queue(self, name=None, channel=None):
"""
Holt events eines Kanals
@type channel: string
@param channel: Kanal (optional)
@rtype: list
@return: Liste der Events
"""
queue = self.rstore.get_event_queue(name, channel)
return queue
# ------------------------------------------------------------------------------------------ #
def get_events(self, name=None, channel=None):
"""
Holt events eines Kanals
@type channel: string
@param channel: Kanal (optional)
@rtype: list
@return: Liste der Events
"""
events = self.rstore.get_events(name, channel)
return events
# ------------------------------------------------------------------------------------------ #
def get_event(self, name=None, channel=None):
"""
Holt event eines Kanals
@type channel: string
@param channel: Kanal (optional)
@rtype: dict
@return: Event
"""
events = self.rstore.get_events(name, channel)
result = False
if events:
result = events.pop(0)
return result
# ------------------------------------------------------------------------------------------ #
def send_admin_mail(self, level, message, state):
"""
Sendent mail an Admin(s),
@type message: string
@param message: Die Message
@type state: dict
@param state: Der State
@return result
"""
if self.fromMail and self.adminMails:
subject = "Possible comba problem on job " + state['job'] + " - " + level
mailmessage = "Hi Admin,\n comba reports a possible problem\n\n"
mailmessage = mailmessage + level + "!\n"
mailmessage = mailmessage + message + "\n\n"
mailmessage = mailmessage + "Additional information:\n"
mailmessage = mailmessage + "##################################################\n"
mailmessage = mailmessage + "Job:\t" + state['job'] + "\n"
mailmessage = mailmessage + "Code:\t" + state['code'] + "\n"
mailmessage = mailmessage + "Value:\t" + str(state['value']) + "\n"
mailer = AuraMailer(self.adminMails, self.fromMail)
mailer.sendAdminMail(subject,mailmessage)
else:
return False
# ------------------------------------------------------------------------------------------ #
def receive(self):
"""
Bisher wird nichts empfangen
"""
return ""
# ------------------------------------------------------------------------------------------ #
def get_next_file_for_fallback(self, fallbacktype):
next = self.rstore.db.get('next_'+fallbacktype+'_fallback_file')
if next is None:
raise FallbackException("No next file found for "+fallbacktype+"-fallback")
return next.decode('utf-8')
# ------------------------------------------------------------------------------------------ #
def set_next_file_for_fallback(self, fallbacktype, file):
self.rstore.db.set("next_" + fallbacktype + "_fallback_file", file)