2018-12-05 Fred Gleason <fredg@paravelsystems.com>

* Added a set of Python classes for processing PAD updates.
This commit is contained in:
Fred Gleason 2018-12-05 20:15:10 -05:00
parent fc0023a0cd
commit 1d8e303101
12 changed files with 356 additions and 4 deletions

View File

@ -18100,3 +18100,5 @@
2018-12-05 Fred Gleason <fredg@paravelsystems.com>
* Added a set of enclosing '{}' braces around the JSON-formatted PAD
output to make it well-formed.
2018-12-05 Fred Gleason <fredg@paravelsystems.com>
* Added a set of Python classes for processing PAD updates.

View File

@ -20,7 +20,8 @@
##
## Use automake to process this into a Makefile.in
SUBDIRS = rivwebcapi\
SUBDIRS = PyPAD\
rivwebcapi\
rlm
CLEANFILES = *~\

39
apis/PyPAD/Makefile.am Normal file
View File

@ -0,0 +1,39 @@
## automake.am
##
## Automake.am for Rivendell PyPAD
##
## (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of
## the License, or (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public
## License along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
##
## Use automake to process this into a Makefile.in
SUBDIRS = api\
examples
CLEANFILES = *~\
*.idb\
*ilk\
*.obj\
*.pdb\
*.qm\
moc_*
MAINTAINERCLEANFILES = *~\
*.tar.gz\
aclocal.m4\
configure\
Makefile.in\
moc_*

View File

@ -0,0 +1,40 @@
## automake.am
##
## Automake.am for Rivendell PyPAD/api
##
## (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of
## the License, or (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public
## License along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
##
## Use automake to process this into a Makefile.in
rivendelldir = $(pkgpyexecdir)
rivendell_PYTHON = __init__.py\
PyPAD.py
CLEANFILES = *~\
*.idb\
*ilk\
*.obj\
*.pdb\
*.qm\
moc_*
MAINTAINERCLEANFILES = *~\
*.tar.gz\
aclocal.m4\
configure\
Makefile.in\
moc_*

198
apis/PyPAD/api/PyPAD.py Normal file
View File

@ -0,0 +1,198 @@
#!/usr/bin/python
# PyPAD.py
#
# PAD processor for Rivendell
#
# (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import socket
import json
class PyPADUpdate(object):
def __init__(self,pad_data):
self.__fields=pad_data;
def __replace(self,wildcard,string):
stype='now'
if wildcard[1].isupper():
stype='next'
sfields={'a':'artist','b':'label','c':'client','d':'','e':'agency',
'f':'','g':'groupName','h':'length','i':'description',
'j':'cutNumber','k':'','l':'album','m': 'composer',
'n':'cartNumber','o':'outcue','p':'publisher','q': '',
'r':'conductor','s':'songId','t':'title','u':'userDefined',
'v':'','w':'','x':'','y':'year',
'z':''}
sfield=sfields[wildcard[1].lower()]
try:
string=string.replace(wildcard,self.__fields['padUpdate'][stype][sfield])
except TypeError:
string=string.replace(wildcard,'')
except KeyError:
string=string.replace(wildcard,'')
return string
def dateTime(self):
"""
Returns the date-time stamp of the update in RFC-822 format (string).
"""
return self.__fields['padUpdate']['dateTime']
def logMachine(self):
"""
Returns the log machine number to which this update pertains
(integer).
"""
return self.__fields['padUpdate']['logMachine']
def onairFlag(self):
"""
Returns the state of the on-air flag (boolean).
"""
return self.__fields['padUpdate']['onairFlag']
def hasService(self):
"""
Indicates if service information is included with this update
(boolean).
"""
try:
return self.__fields['padUpdate']['service']!=None
except TypeError:
return False;
def serviceName(self):
"""
Returns the name of the service associated with this update (string).
"""
return self.__fields['padUpdate']['service']['name']
def serviceDescription(self):
"""
Returns the description of the service associated with this update
(string).
"""
return self.__fields['padUpdate']['service']['description']
def serviceProgramCode(self):
"""
Returns the Program Code of the service associated with this update
(string).
"""
return self.__fields['padUpdate']['service']['programCode']
def hasLog(self):
"""
Indicates if log information is included with this update
(boolean).
"""
try:
return self.__fields['padUpdate']['log']!=None
except TypeError:
return False;
def logName(self):
"""
Returns the name of the log associated with this update (string).
"""
return self.__fields['padUpdate']['log']['name']
def padFields(self,string):
"""
Takes an argument of a string containing one or more PAD wildcards,
which it will resolve into the appropriate values. See the
'Metadata Wildcards' section of the Rivendell Operations Guide
for a list of recognized wildcards.
"""
for i in range(65,68):
string=self.__replace('%'+chr(i),string)
for i in range(69,91):
string=self.__replace('%'+chr(i),string)
for i in range(97,100):
string=self.__replace('%'+chr(i),string)
for i in range(101,123):
string=self.__replace('%'+chr(i),string)
return string
def hasNowPad(self):
"""
Indicates if this update include 'Now' playing PAD.
"""
try:
return self.__fields['padUpdate']['now']!=None
except TypeError:
return False;
def hasNextPad(self):
"""
Indicates if this update include 'Next' playing PAD.
"""
try:
return self.__fields['padUpdate']['next']!=None
except TypeError:
return False;
class PyPADReceiver(object):
def __init__(self):
self.__callback=None
self.__priv=None
def __PyPAD_Process(self,pad):
self.__callback(pad,self.__priv)
def setCallback(self,cb):
"""
Set the processing callback.
"""
self.__callback=cb
def setPrivateObject(self,obj):
"""
Set the 'private' object. This object will be passed to the
callback in its second argument.
"""
self.priv=obj
def start(self,hostname):
"""
Connect to a Rivendell system and begin processing PAD events.
Once started, a PyPAD object can be interacted with
only within one of its callback methods.
Takes the following argument:
hostname: The hostname or IP address of the Rivendell system.
"""
sock=socket.socket(socket.AF_INET)
conn=sock.connect((hostname,34289))
c=""
line=""
msg=""
while 1<2:
c=sock.recv(1)
line+=c
if c[0]=="\n":
msg+=line
if line=="\r\n":
self.__PyPAD_Process(PyPADUpdate(json.loads(msg)))
msg=""
line=""

View File

View File

@ -0,0 +1,38 @@
## automake.am
##
## Automake.am for Rivendell PyPAD/examples
##
## (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of
## the License, or (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public
## License along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
##
## Use automake to process this into a Makefile.in
EXTRA_DIST = now_and_next.py
CLEANFILES = *~\
*.idb\
*ilk\
*.obj\
*.pdb\
*.qm\
moc_*
MAINTAINERCLEANFILES = *~\
*.tar.gz\
aclocal.m4\
configure\
Makefile.in\
moc_*

View File

@ -0,0 +1,18 @@
#!/usr/bin/python
import rivendell.PyPAD
def ProcessPad(update,priv):
print
if update.hasNowPad():
print update.padFields("NOW: %a - %t")
else:
print "NOW: [none]"
if update.hasNextPad():
print update.padFields("NEXT: %A - %T")
else:
print "NEXT: [none]"
rcvr=rivendell.PyPAD.PyPADReceiver()
rcvr.setCallback(ProcessPad)
rcvr.start("localhost")

View File

@ -247,6 +247,11 @@ AC_CHECK_HEADER(security/pam_appl.h,[],[AC_MSG_ERROR([*** PAM not found ***])])
#
AC_CHECK_HEADER(soundtouch/SoundTouch.h,[],[AC_MSG_ERROR([*** SoundTouch not found ***])])
#
# Check for Python
#
AM_PATH_PYTHON([2.7])
#
# Check for FLAC
#
@ -462,6 +467,9 @@ AC_CONFIG_FILES([rivendell.spec \
icons/Makefile \
helpers/Makefile \
apis/Makefile \
apis/PyPAD/Makefile \
apis/PyPAD/api/Makefile \
apis/PyPAD/examples/Makefile \
apis/rivwebcapi/Makefile \
apis/rivwebcapi/rivwebcapi.pc \
apis/rivwebcapi/rivwebcapi/Makefile \

View File

@ -991,7 +991,15 @@ int RDTimeZoneOffset()
tm=gmtime(&t);
time_t gmt_time=3600*tm->tm_hour+60*tm->tm_min+tm->tm_sec;
return gmt_time-local_time;
int offset=gmt_time-local_time;
if(offset>43200) {
offset=offset-86400;
}
if(offset<-43200) {
offset=offset+86400;
}
return offset;
}

View File

@ -2964,7 +2964,7 @@ void RDLogPlay::SendNowNext()
play_pad_socket->write(QString("{\r\n").toUtf8());
play_pad_socket->write(QString(" \"padUpdate\": {\r\n").toUtf8());
play_pad_socket->write(RDJsonField("dateTime",QDateTime::currentDateTime(),8).toUtf8());
play_pad_socket->write(RDJsonField("logMachine",play_id,8));
play_pad_socket->write(RDJsonField("logMachine",play_id+1,8));
play_pad_socket->write(RDJsonField("onairFlag",play_onair_flag,8));
play_pad_socket->write(RDJsonField("logMode",RDAirPlayConf::logModeText(play_op_mode),8));

View File

@ -1213,7 +1213,7 @@ QString RDWebDateTime(const QDateTime &datetime)
tzstr="GMT";
}
return RDLocalToUtc(datetime).toString("ddd, dd MMM yyyy hh:mm:ss")+" "+tzstr;
return datetime.toString("ddd, dd MMM yyyy hh:mm:ss")+" "+tzstr;
}