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 2140 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 Boolean, Column, Date, DateTime, Float, Integer, String, Text, Time, ForeignKey, ForeignKeyConstraint
from sqlalchemy.orm import relationship
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
@property
def serialize(self):
"""Return object data in easily serializeable format"""
return {
'schedule_id': self.schedule_id,
'show_id': self.show_id,
'schedule_start': self.dump_datetime(self.schedule_start),
'schedule_end': self.dump_datetime(self.schedule_end),
'show_name': self.show_name,
'show_hosts': self.show_hosts,
'is_repetition': self.is_repetition,
'fallback_playlist': self.fallback_playlist,
'fallback_pool': self.fallback_pool,
'station_fallback_pool': self.station_fallback_pool
}
# ------------------------------------------------------------------------------------------ #
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, nullable=False)
entry_end = Column(DateTime)
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)
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)
if self.entry_start is not None:
self.calc_unix_times()
def calc_unix_times(self):
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().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():
from sqlalchemy import func
# damn BAND-AID
#db.session.commit()
#subqry = db.session.query(func.max(ScheduleEntry.entry_num)).filter(ScheduleEntry.schedule_id = 0)
#qry = db.session.query(Data).filter(Data.user_id == user_id, Data.counter == subqry)
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 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
This diff is collapsed.
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))
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
This diff is collapsed.
This diff is collapsed.