mirror of
https://github.com/ElvishArtisan/rivendell.git
synced 2025-10-14 14:41:13 +02:00
2019-01-08 Fred Gleason <fredg@paravelsystems.com>
* Changed the PyPAD namespace from 'PyPAD' to 'pypad'. * Changed the installation location of PyPAD scripts from '@libdir@/rivendell/PyPAD/' to '@libdir@/rivendell/pypad/'.
This commit is contained in:
138
apis/pypad/scripts/Makefile.am
Normal file
138
apis/pypad/scripts/Makefile.am
Normal file
@@ -0,0 +1,138 @@
|
||||
## makefile.am
|
||||
##
|
||||
## Makefile.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
|
||||
|
||||
install-exec-am:
|
||||
mkdir -p $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad
|
||||
../../../helpers/install_python.sh pypad_ando.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_ando.py
|
||||
cp pypad_ando.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_ando.exemplar
|
||||
../../../helpers/install_python.sh pypad_filewrite.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_filewrite.py
|
||||
cp pypad_filewrite.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_filewrite.exemplar
|
||||
../../../helpers/install_python.sh pypad_icecast2.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_icecast2.py
|
||||
cp pypad_icecast2.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_icecast2.exemplar
|
||||
../../../helpers/install_python.sh pypad_inno713.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_inno713.py
|
||||
cp pypad_inno713.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_inno713.exemplar
|
||||
../../../helpers/install_python.sh pypad_live365.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_live365.py
|
||||
cp pypad_live365.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_live365.exemplar
|
||||
../../../helpers/install_python.sh pypad_liqcomp.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_liqcomp.py
|
||||
cp pypad_liqcomp.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_liqcomp.exemplar
|
||||
../../../helpers/install_python.sh pypad_serial.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_serial.py
|
||||
cp pypad_serial.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_serial.exemplar
|
||||
../../../helpers/install_python.sh pypad_shoutcast1.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_shoutcast1.py
|
||||
cp pypad_shoutcast1.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_shoutcast1.exemplar
|
||||
../../../helpers/install_python.sh pypad_spinitron.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_spinitron.py
|
||||
cp pypad_spinitron.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_spinitron.exemplar
|
||||
../../../helpers/install_python.sh pypad_spottrap.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_spottrap.py
|
||||
cp pypad_spottrap.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_spottrap.exemplar
|
||||
../../../helpers/install_python.sh pypad_tunein.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_tunein.py
|
||||
cp pypad_tunein.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_tunein.exemplar
|
||||
../../../helpers/install_python.sh pypad_udp.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_udp.py
|
||||
cp pypad_udp.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_udp.exemplar
|
||||
../../../helpers/install_python.sh pypad_urlwrite.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_urlwrite.py
|
||||
cp pypad_urlwrite.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_urlwrite.exemplar
|
||||
../../../helpers/install_python.sh pypad_walltime.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_walltime.py
|
||||
cp pypad_walltime.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_walltime.exemplar
|
||||
../../../helpers/install_python.sh pypad_xds.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_xds.py
|
||||
cp pypad_xds.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_xds.exemplar
|
||||
../../../helpers/install_python.sh pypad_xmpad.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_xmpad.py
|
||||
cp pypad_xmpad.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_xmpad.exemplar
|
||||
|
||||
uninstall-local:
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_ando.exemplar
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_ando.py
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_filewrite.exemplar
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_filewrite.py
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_icecast2.exemplar
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_icecast2.py
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_inno713.exemplar
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_inno713.py
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_live365.exemplar
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_live365.py
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_liqcomp.exemplar
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_liqcomp.py
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_serial.exemplar
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_serial.py
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_shoutcast1.exemplar
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_shoutcast1.py
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_spinitron.exemplar
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_spinitron.py
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_spottrap.exemplar
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_spottrap.py
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_tunein.exemplar
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_tunein.py
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_udp.exemplar
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_udp.py
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_urlwrite.exemplar
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_urlwrite.py
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_walltime.exemplar
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_walltime.py
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_xds.exemplar
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_xds.py
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_xmpad.exemplar
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_xmpad.py
|
||||
|
||||
EXTRA_DIST = pypad_ando.exemplar\
|
||||
pypad_ando.py\
|
||||
pypad_filewrite.exemplar\
|
||||
pypad_filewrite.py\
|
||||
pypad_icecast2.exemplar\
|
||||
pypad_icecast2.py\
|
||||
pypad_inno713.exemplar\
|
||||
pypad_inno713.py\
|
||||
pypad_liqcomp.exemplar\
|
||||
pypad_liqcomp.py\
|
||||
pypad_live365.exemplar\
|
||||
pypad_live365.py\
|
||||
pypad_serial.exemplar\
|
||||
pypad_serial.py\
|
||||
pypad_shoutcast1.exemplar\
|
||||
pypad_shoutcast1.py\
|
||||
pypad_spinitron.exemplar\
|
||||
pypad_spinitron.py\
|
||||
pypad_spottrap.exemplar\
|
||||
pypad_spottrap.py\
|
||||
pypad_tunein.exemplar\
|
||||
pypad_tunein.py\
|
||||
pypad_udp.exemplar\
|
||||
pypad_udp.py\
|
||||
pypad_urlwrite.exemplar\
|
||||
pypad_urlwrite.py\
|
||||
pypad_walltime.exemplar\
|
||||
pypad_walltime.py\
|
||||
pypad_xds.exemplar\
|
||||
pypad_xds.py\
|
||||
pypad_xmpad.exemplar\
|
||||
pypad_xmpad.py
|
||||
|
||||
CLEANFILES = *~\
|
||||
*.idb\
|
||||
*ilk\
|
||||
*.obj\
|
||||
*.pdb\
|
||||
*.qm\
|
||||
moc_*
|
||||
|
||||
MAINTAINERCLEANFILES = *~\
|
||||
*.tar.gz\
|
||||
aclocal.m4\
|
||||
configure\
|
||||
Makefile.in\
|
||||
moc_*
|
99
apis/pypad/scripts/pypad_ando.exemplar
Normal file
99
apis/pypad/scripts/pypad_ando.exemplar
Normal file
@@ -0,0 +1,99 @@
|
||||
; This is the configuration for the 'pypad_ando.py' script for
|
||||
; Rivendell, which can be used to output Now & Next data to one or more
|
||||
; ANDO Media Streaming systems, using one of the following formats:
|
||||
;
|
||||
; ^<artist>~<title>~<duration>~<group>~<album>~<cartnum>|
|
||||
; ^<artist>~<title>~<duration>~<group>~<cartnum>~<album>~<label>|
|
||||
|
||||
; Section Header
|
||||
;
|
||||
; One section per remote ANDO system is configured, starting with 'System1' and
|
||||
; working up consecutively
|
||||
[System1]
|
||||
|
||||
; IP Address
|
||||
;
|
||||
; The IP address of the remote ANDO port, in dotted-quad notation.
|
||||
IpAddress=192.168.10.29
|
||||
|
||||
; UDP Port
|
||||
;
|
||||
; The UDP port number of the remote ANDO system, in the range 0 - 65,535.
|
||||
UdpPort=5273
|
||||
|
||||
; Field Definitions. The string to use to populate the <title>, <artist>,
|
||||
; <album> and <label> fields sent to ANDO each time RDAirPlay changes play
|
||||
; state. These can include wildcards as placeholders for metadata values.
|
||||
; For the list of supported wildcards. see the 'Metadata Wildcards' section
|
||||
; of the Rivendell Operations Guide.
|
||||
;
|
||||
; The <label> field is optional, and should be left blank unless you know
|
||||
; that your specific ANDO configuration requires it.
|
||||
;
|
||||
Title=%t
|
||||
Artist=%a
|
||||
Album=%l
|
||||
Label=
|
||||
|
||||
; Log Selection
|
||||
;
|
||||
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
|
||||
; state changes on that log should be output on this udp port. If set
|
||||
; to 'Onair', then output will be generated only if RDAirPlay's OnAir flag
|
||||
; is active.
|
||||
MasterLog=Yes
|
||||
Aux1Log=Yes
|
||||
Aux2Log=Yes
|
||||
VLog101=No
|
||||
VLog102=No
|
||||
VLog103=No
|
||||
VLog104=No
|
||||
VLog105=No
|
||||
VLog106=No
|
||||
VLog107=No
|
||||
VLog108=No
|
||||
VLog109=No
|
||||
VLog110=No
|
||||
VLog111=No
|
||||
VLog112=No
|
||||
VLog113=No
|
||||
VLog114=No
|
||||
VLog115=No
|
||||
VLog116=No
|
||||
VLog117=No
|
||||
VLog118=No
|
||||
VLog119=No
|
||||
VLog120=No
|
||||
|
||||
|
||||
; Additional ANDO systems can be configured by adding new sections...
|
||||
;[System2]
|
||||
;IpAddress=192.168.10.22
|
||||
;UdpPort=6789
|
||||
;Title=%u
|
||||
;Artist=%a
|
||||
;Album=%p
|
||||
;Label=
|
||||
;MasterLog=Yes
|
||||
;Aux1Log=No
|
||||
;Aux2Log=Onair
|
||||
;VLog101=No
|
||||
;VLog102=No
|
||||
;VLog103=No
|
||||
;VLog104=No
|
||||
;VLog105=No
|
||||
;VLog106=No
|
||||
;VLog107=No
|
||||
;VLog108=No
|
||||
;VLog109=No
|
||||
;VLog110=No
|
||||
;VLog111=No
|
||||
;VLog112=No
|
||||
;VLog113=No
|
||||
;VLog114=No
|
||||
;VLog115=No
|
||||
;VLog116=No
|
||||
;VLog117=No
|
||||
;VLog118=No
|
||||
;VLog119=No
|
||||
;VLog120=No
|
88
apis/pypad/scripts/pypad_ando.py
Executable file
88
apis/pypad/scripts/pypad_ando.py
Executable file
@@ -0,0 +1,88 @@
|
||||
#!%PYTHON_BANGPATH%
|
||||
|
||||
# pypad_ando.py
|
||||
#
|
||||
# Send PAD updates to an Ando AdInjector
|
||||
#
|
||||
# (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 sys
|
||||
import socket
|
||||
import configparser
|
||||
import pypad
|
||||
|
||||
last_updates={}
|
||||
|
||||
def eprint(*args,**kwargs):
|
||||
print(*args,file=sys.stderr,**kwargs)
|
||||
|
||||
def ProcessTimer(config):
|
||||
n=1
|
||||
while(True):
|
||||
section='System'+str(n)
|
||||
try:
|
||||
send_sock.sendto('HB'.encode('utf-8'),
|
||||
(config.get(section,'IpAddress'),int(config.get(section,'UdpPort'))))
|
||||
n=n+1
|
||||
except configparser.NoSectionError:
|
||||
return
|
||||
|
||||
def ProcessPad(update):
|
||||
try:
|
||||
last_updates[update.machine()]
|
||||
except KeyError:
|
||||
last_updates[update.machine()]=None
|
||||
|
||||
n=1
|
||||
while(True):
|
||||
section='System'+str(n)
|
||||
try:
|
||||
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW) and (last_updates[update.machine()] != update.startDateTimeString(pypad.TYPE_NOW)):
|
||||
last_updates[update.machine()]=update.startDateTimeString(pypad.TYPE_NOW)
|
||||
title=update.resolvePadFields(update.config().get(section,'Title'),pypad.ESCAPE_NONE)
|
||||
artist=update.resolvePadFields(update.config().get(section,'Artist'),pypad.ESCAPE_NONE)
|
||||
album=update.resolvePadFields(update.config().get(section,'Album'),pypad.ESCAPE_NONE)
|
||||
label=update.resolvePadFields(update.config().get(section,'Label'),pypad.ESCAPE_NONE)
|
||||
secs=update.padField(pypad.TYPE_NOW,pypad.FIELD_LENGTH)
|
||||
duration=('%02d:' % (secs//60000))+('%02d' % ((secs%60000)//1000))
|
||||
group=update.padField(pypad.TYPE_NOW,pypad.FIELD_GROUP_NAME)
|
||||
if update.config().get(section,'Label')=='':
|
||||
msg='^'+artist+'~'+title+'~'+duration+'~'+group+'~'+album+'~'+str(update.padField(pypad.TYPE_NOW,pypad.FIELD_CART_NUMBER))+'|'
|
||||
else:
|
||||
msg='^'+artist+'~'+title+'~'+duration+'~'+group+'~'+str(update.padField(pypad.TYPE_NOW,pypad.FIELD_CART_NUMBER))+'~'+album+'~'+label+'|'
|
||||
send_sock.sendto(msg.encode('utf-8'),
|
||||
(update.config().get(section,'IpAddress'),int(update.config().get(section,'UdpPort'))))
|
||||
n=n+1
|
||||
except configparser.NoSectionError:
|
||||
return
|
||||
|
||||
#
|
||||
# 'Main' function
|
||||
#
|
||||
# Create Send Socket
|
||||
#
|
||||
send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
|
||||
|
||||
rcvr=pypad.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_ando.py: USAGE: cmd <hostname> <port> <config>')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
rcvr.setTimerCallback(30,ProcessTimer)
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
101
apis/pypad/scripts/pypad_filewrite.exemplar
Normal file
101
apis/pypad/scripts/pypad_filewrite.exemplar
Normal file
@@ -0,0 +1,101 @@
|
||||
; This is the sample configuration for the 'pypad_filewrite.py' PyPAD script
|
||||
; for Rivendell, which can be used to write one or more files on the local
|
||||
; system using Now & Next data.
|
||||
;
|
||||
|
||||
; Section Header
|
||||
;
|
||||
; One section per file to be written should be configured, starting with
|
||||
; 'File1' and working up consecutively
|
||||
[File1]
|
||||
|
||||
; Filename
|
||||
;
|
||||
; The full path to the file to be written. The filename may contain filepath
|
||||
; wildcards as defined in Appendix C of the Rivendell Operations and
|
||||
; Administration Guide. The user running RDAirPlay must have write
|
||||
; permissions for this location.
|
||||
Filename=/tmp/rlm_filewrite.txt
|
||||
|
||||
; Append Mode
|
||||
;
|
||||
; If set to '0', the file will be completely overwritten with the contents
|
||||
; of each PAD update. If set to '1', each update will be appended to the
|
||||
; existing contents of the file.
|
||||
Append=0
|
||||
|
||||
; Format String. The string to be output each time RDAirPlay changes
|
||||
; play state, including any wildcards as placeholders for metadata values.
|
||||
;
|
||||
; The list of available wildcards can be found in the 'metadata_wildcards.txt'
|
||||
; file in the Rivendell documentation directory.
|
||||
;
|
||||
FormatString=NOW: %d(ddd MMM d hh:mm:ss yyyy): %t - %a\nNEXT: %D(ddd MMM d hh:mm:ss yyyy): %T - %A\n
|
||||
|
||||
; Encoding. Defines the set of escapes to be applied to the PAD fields.
|
||||
; The following options are available:
|
||||
;
|
||||
; 0 - Perform no character escaping.
|
||||
; 1 - "XML" escaping: Escape reserved characters as per XML-v1.0
|
||||
; 2 - "Web" escaping: Escape reserved characters as per RFC 2396 Section 2.4
|
||||
Encoding=0
|
||||
|
||||
; Log Selection
|
||||
;
|
||||
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
|
||||
; state changes on that log should be output. If set to 'Onair', then
|
||||
; output will be generated only if RDAirPlays OnAir flag is active.
|
||||
MasterLog=Yes
|
||||
Aux1Log=Yes
|
||||
Aux2Log=Yes
|
||||
VLog101=No
|
||||
VLog102=No
|
||||
VLog103=No
|
||||
VLog104=No
|
||||
VLog105=No
|
||||
VLog106=No
|
||||
VLog107=No
|
||||
VLog108=No
|
||||
VLog109=No
|
||||
VLog110=No
|
||||
VLog111=No
|
||||
VLog112=No
|
||||
VLog113=No
|
||||
VLog114=No
|
||||
VLog115=No
|
||||
VLog116=No
|
||||
VLog117=No
|
||||
VLog118=No
|
||||
VLog119=No
|
||||
VLog120=No
|
||||
|
||||
|
||||
; Additional files can be written by adding new sections...
|
||||
;
|
||||
;[File2]
|
||||
;Filename=/home/rd/foo2.txt
|
||||
;Append=1
|
||||
;FormatString=%t by %a\r\n
|
||||
;MasterLog=Yes
|
||||
;Aux1Log=No
|
||||
;Aux2Log=Onair
|
||||
;VLog101=No
|
||||
;VLog102=No
|
||||
;VLog103=No
|
||||
;VLog104=No
|
||||
;VLog105=No
|
||||
;VLog106=No
|
||||
;VLog107=No
|
||||
;VLog108=No
|
||||
;VLog109=No
|
||||
;VLog110=No
|
||||
;VLog111=No
|
||||
;VLog112=No
|
||||
;VLog113=No
|
||||
;VLog114=No
|
||||
;VLog115=No
|
||||
;VLog116=No
|
||||
;VLog117=No
|
||||
;VLog118=No
|
||||
;VLog119=No
|
||||
;VLog120=No
|
58
apis/pypad/scripts/pypad_filewrite.py
Executable file
58
apis/pypad/scripts/pypad_filewrite.py
Executable file
@@ -0,0 +1,58 @@
|
||||
#!%PYTHON_BANGPATH%
|
||||
|
||||
# pypad_filewrite.py
|
||||
#
|
||||
# Write PAD updates to files
|
||||
#
|
||||
# (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 sys
|
||||
import configparser
|
||||
import pypad
|
||||
|
||||
def eprint(*args,**kwargs):
|
||||
print(*args,file=sys.stderr,**kwargs)
|
||||
|
||||
def ProcessPad(update):
|
||||
n=1
|
||||
try:
|
||||
while(True):
|
||||
section='File'+str(n)
|
||||
if update.shouldBeProcessed(section):
|
||||
fmtstr=update.config().get(section,'FormatString')
|
||||
mode='w'
|
||||
if update.config().get(section,'Append')=='1':
|
||||
mode='a'
|
||||
f=open(update.resolveFilepath(update.config().get(section,'Filename'),update.dateTime()),mode)
|
||||
f.write(update.resolvePadFields(fmtstr,int(update.config().get(section,'Encoding'))))
|
||||
f.close()
|
||||
n=n+1
|
||||
|
||||
except configparser.NoSectionError:
|
||||
return
|
||||
|
||||
#
|
||||
# 'Main' function
|
||||
#
|
||||
rcvr=pypad.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_filewrite.py: USAGE: cmd <hostname> <port> <config>')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
106
apis/pypad/scripts/pypad_icecast2.exemplar
Normal file
106
apis/pypad/scripts/pypad_icecast2.exemplar
Normal file
@@ -0,0 +1,106 @@
|
||||
; This is the sample configuration for the 'pypad_icecast2.py' PyPAD script
|
||||
; for Rivendell, which can be used to update the metadata on an Icecast2
|
||||
; mountpoint using Now & Next data.
|
||||
;
|
||||
|
||||
; Section Header
|
||||
;
|
||||
; One section per Icecast2 mountpoint is configured, starting with
|
||||
; 'Icecast1' and working up consecutively
|
||||
[Icecast1]
|
||||
|
||||
; User Name
|
||||
;
|
||||
; The username of the Icecast2 account to which to send updates.
|
||||
Username=source
|
||||
|
||||
; Password
|
||||
;
|
||||
; The password of the Icecast2 account to which to send updates.
|
||||
Password=hackme
|
||||
|
||||
; Host Name
|
||||
;
|
||||
; The fully-qualified domain name or IP address of the Icecast2 server
|
||||
Hostname=icecast.example.com
|
||||
|
||||
; Host Port
|
||||
;
|
||||
; The TCP port number of the Icecast2 server
|
||||
Tcpport=8000
|
||||
|
||||
; Mountpoint
|
||||
;
|
||||
; The Icecast2 mountpoint
|
||||
Mountpoint=/audio.mp3
|
||||
|
||||
; Format String. The metadata to be sent each time RDAirPlay changes
|
||||
; play state, including any wildcards as placeholders for metadata values.
|
||||
;
|
||||
; The list of available wildcards can be found in the 'metadata_wildcards.txt'
|
||||
; file in the Rivendell documentation directory.
|
||||
;
|
||||
FormatString=%a - %t
|
||||
|
||||
; Log Selection
|
||||
;
|
||||
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
|
||||
; state changes on that log should be output to this account. If set
|
||||
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
|
||||
; is active.
|
||||
MasterLog=Yes
|
||||
Aux1Log=Yes
|
||||
Aux2Log=Yes
|
||||
VLog101=No
|
||||
VLog102=No
|
||||
VLog103=No
|
||||
VLog104=No
|
||||
VLog105=No
|
||||
VLog106=No
|
||||
VLog107=No
|
||||
VLog108=No
|
||||
VLog109=No
|
||||
VLog110=No
|
||||
VLog111=No
|
||||
VLog112=No
|
||||
VLog113=No
|
||||
VLog114=No
|
||||
VLog115=No
|
||||
VLog116=No
|
||||
VLog117=No
|
||||
VLog118=No
|
||||
VLog119=No
|
||||
VLog120=No
|
||||
|
||||
|
||||
; Additional Icecast2 mountpoints can be configured by adding new sections...
|
||||
;[Icecast2]
|
||||
;Username=source
|
||||
;Password=letmein
|
||||
;Hostname=anotherone.example.com
|
||||
;Tcpport=80
|
||||
;Mountpoint=moreaudio.mp3
|
||||
;FormatString=%t by %a
|
||||
;MasterLog=Yes
|
||||
;Aux1Log=No
|
||||
;Aux2Log=Onair
|
||||
;VLog101=No
|
||||
;VLog102=No
|
||||
;VLog103=No
|
||||
;VLog104=No
|
||||
;VLog105=No
|
||||
;VLog106=No
|
||||
;VLog107=No
|
||||
;VLog108=No
|
||||
;VLog109=No
|
||||
;VLog110=No
|
||||
;VLog111=No
|
||||
;VLog112=No
|
||||
;VLog113=No
|
||||
;VLog114=No
|
||||
;VLog115=No
|
||||
;VLog116=No
|
||||
;VLog117=No
|
||||
;VLog118=No
|
||||
;VLog119=No
|
||||
;VLog120=No
|
98
apis/pypad/scripts/pypad_icecast2.py
Executable file
98
apis/pypad/scripts/pypad_icecast2.py
Executable file
@@ -0,0 +1,98 @@
|
||||
#!%PYTHON_BANGPATH%
|
||||
|
||||
# pypad_icecast2.py
|
||||
#
|
||||
# Send PAD updates to Icecast2 mountpoint
|
||||
#
|
||||
# (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 os
|
||||
import sys
|
||||
import socket
|
||||
import requests
|
||||
from requests.auth import HTTPBasicAuth
|
||||
import xml.etree.ElementTree as ET
|
||||
import syslog
|
||||
import pypad
|
||||
import configparser
|
||||
|
||||
def eprint(*args,**kwargs):
|
||||
print(pypad_name+': ',file=sys.stderr,end='')
|
||||
print(*args,file=sys.stderr)
|
||||
syslog.syslog(syslog.LOG_ERR,*args)
|
||||
|
||||
def iprint(*args,**kwargs):
|
||||
print(pypad_name+': ',file=sys.stdout,end='')
|
||||
print(*args,file=sys.stdout)
|
||||
syslog.syslog(syslog.LOG_INFO,*args)
|
||||
|
||||
def isTrue(string):
|
||||
l=['Yes','On','True','1']
|
||||
return string.lower() in map(str.lower,l)
|
||||
|
||||
def ProcessPad(update):
|
||||
if update.hasPadType(pypad.TYPE_NOW):
|
||||
n=1
|
||||
while(True):
|
||||
section='Icecast'+str(n)
|
||||
try:
|
||||
values={}
|
||||
values['mount']=update.config().get(section,'Mountpoint')
|
||||
values['song']=update.resolvePadFields(update.config().get(section,'FormatString'),pypad.ESCAPE_NONE)
|
||||
values['mode']='updinfo'
|
||||
iprint('Updating '+update.config().get(section,'Hostname')+': song='+values['song'])
|
||||
url="http://%s:%s/admin/metadata" % (update.config().get(section,'Hostname'),update.config().get(section,'Tcpport'))
|
||||
try:
|
||||
response=requests.get(url,auth=HTTPBasicAuth(update.config().get(section,'Username'),update.config().get(section,'Password')),params=values)
|
||||
response.raise_for_status()
|
||||
except requests.exceptions.RequestException as e:
|
||||
eprint(str(e))
|
||||
n=n+1
|
||||
except configparser.NoSectionError:
|
||||
if(n==1):
|
||||
eprint('No icecast config found')
|
||||
return
|
||||
|
||||
#
|
||||
# Program Name
|
||||
#
|
||||
pypad_name=os.path.basename(__file__)
|
||||
|
||||
#
|
||||
# Open Syslog
|
||||
#
|
||||
syslog.openlog(pypad_name,logoption=syslog.LOG_PID,facility=syslog.LOG_DAEMON)
|
||||
|
||||
|
||||
#
|
||||
# Create Send Socket
|
||||
#
|
||||
send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
|
||||
|
||||
#
|
||||
# Start Receiver
|
||||
#
|
||||
rcvr=pypad.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_icecast2: USAGE: cmd <hostname> <port> <config>')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
iprint('Started')
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
||||
iprint('Stopped')
|
117
apis/pypad/scripts/pypad_inno713.exemplar
Normal file
117
apis/pypad/scripts/pypad_inno713.exemplar
Normal file
@@ -0,0 +1,117 @@
|
||||
; This is the configuration for the 'rlm_inno713.py' script for
|
||||
; Rivendell, which can be used to output Now & Next data to one or more
|
||||
; Innovonics model 713 RDS encoders.
|
||||
|
||||
; Section Header
|
||||
;
|
||||
; One section per remote RDS unit is configured, starting with 'Rds1' and
|
||||
; working up consecutively
|
||||
[Rds1]
|
||||
|
||||
; Two methods of connecting to the unit are supported: TCP/IP or serial.
|
||||
;
|
||||
; *****************************************************************************
|
||||
; TCP/IP Connection Settings
|
||||
; IP Address
|
||||
;
|
||||
; The IP address of the UDP port to send updates to, in dotted-quad notation.
|
||||
; If using a serial connection, leave this entry blank!
|
||||
IpAddress=127.0.0.1
|
||||
|
||||
; UDP Port
|
||||
;
|
||||
; The UDP port number to send updates to, in the range 0 - 65,535.
|
||||
UdpPort=10001
|
||||
; *****************************************************************************
|
||||
|
||||
; *****************************************************************************
|
||||
; Serial Connection Settings
|
||||
;
|
||||
; The device file that corresponds to the serial device that is connected
|
||||
; to the unit. If using a TCP/IP connection, leave this entry blank!
|
||||
;Device=/dev/ttyS0
|
||||
|
||||
; Serial Baud Rate (in bps)
|
||||
Speed=9600
|
||||
|
||||
; Parity (0=none, 1=even, 2=odd)
|
||||
Parity=0
|
||||
|
||||
; Number of bits per data 'word'.
|
||||
WordSize=8
|
||||
; *****************************************************************************
|
||||
|
||||
; Output Strings. The PAD data to output each time RDAirPlay changes
|
||||
; play state, including any wildcards as placeholders for metadata values.
|
||||
;
|
||||
; For the list of supported wildcards. see the 'Metadata Wildcards' section
|
||||
; of the Rivendell Operations Guide.
|
||||
PsString=
|
||||
DynamicPsString=%t
|
||||
RadiotextString=%a
|
||||
|
||||
; Log Selection
|
||||
;
|
||||
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
|
||||
; state changes on that log should be output on this udp port. If set
|
||||
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
|
||||
; is active.
|
||||
MasterLog=Yes
|
||||
Aux1Log=Yes
|
||||
Aux2Log=Yes
|
||||
VLog101=No
|
||||
VLog102=No
|
||||
VLog103=No
|
||||
VLog104=No
|
||||
VLog105=No
|
||||
VLog106=No
|
||||
VLog107=No
|
||||
VLog108=No
|
||||
VLog109=No
|
||||
VLog110=No
|
||||
VLog111=No
|
||||
VLog112=No
|
||||
VLog113=No
|
||||
VLog114=No
|
||||
VLog115=No
|
||||
VLog116=No
|
||||
VLog117=No
|
||||
VLog118=No
|
||||
VLog119=No
|
||||
VLog120=No
|
||||
|
||||
|
||||
; Additional RDS encoders can be configured by adding new sections...
|
||||
;[Rds2]
|
||||
;IpAddress=192.168.10.22
|
||||
;UdpPort=6789
|
||||
;Device=/dev/ttyS0
|
||||
;Speed=9600
|
||||
;Parity=0
|
||||
;WordSize=8
|
||||
;PsString=
|
||||
;DynamicPsString=%t
|
||||
;RadiotextString=%a
|
||||
;MasterLog=Yes
|
||||
;Aux1Log=No
|
||||
;Aux2Log=Onair
|
||||
;VLog101=No
|
||||
;VLog102=No
|
||||
;VLog103=No
|
||||
;VLog104=No
|
||||
;VLog105=No
|
||||
;VLog106=No
|
||||
;VLog107=No
|
||||
;VLog108=No
|
||||
;VLog109=No
|
||||
;VLog110=No
|
||||
;VLog111=No
|
||||
;VLog112=No
|
||||
;VLog113=No
|
||||
;VLog114=No
|
||||
;VLog115=No
|
||||
;VLog116=No
|
||||
;VLog117=No
|
||||
;VLog118=No
|
||||
;VLog119=No
|
||||
;VLog120=No
|
99
apis/pypad/scripts/pypad_inno713.py
Executable file
99
apis/pypad/scripts/pypad_inno713.py
Executable file
@@ -0,0 +1,99 @@
|
||||
#!%PYTHON_BANGPATH%
|
||||
|
||||
# pypad_inno713.py
|
||||
#
|
||||
# Send Now & Next updates to an Innovonics 713 RDS encoder
|
||||
#
|
||||
# (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 sys
|
||||
import socket
|
||||
import configparser
|
||||
import serial
|
||||
import pypad
|
||||
|
||||
def eprint(*args,**kwargs):
|
||||
print(*args,file=sys.stderr,**kwargs)
|
||||
|
||||
|
||||
def ProcessPad(update):
|
||||
n=1
|
||||
while(True):
|
||||
section='Rds'+str(n)
|
||||
try:
|
||||
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW):
|
||||
dps=''
|
||||
if(len(update.config().get(section,'DynamicPsString'))!=0):
|
||||
dps='DPS='+update.resolvePadFields(update.config().get(section,'DynamicPsString'),pypad.ESCAPE_NONE)+'\r\n'
|
||||
ps=''
|
||||
if(len(update.config().get(section,'PsString'))!=0):
|
||||
ps='PS='+update.resolvePadFields(update.config().get(section,'PsString'),pypad.ESCAPE_NONE)+'\r\n'
|
||||
text=''
|
||||
if(len(update.config().get(section,'RadiotextString'))!=0):
|
||||
text='TEXT='+update.resolvePadFields(update.config().get(section,'RadiotextString'),pypad.ESCAPE_NONE)+'\r\n'
|
||||
try:
|
||||
#
|
||||
# Use serial output
|
||||
#
|
||||
tty_dev=update.config().get(section,'Device')
|
||||
speed=int(update.config().get(section,'Speed'))
|
||||
parity=serial.PARITY_NONE
|
||||
if int(update.config().get(section,'Parity'))==1:
|
||||
parity=serial.PARITY_EVEN
|
||||
if int(update.config().get(section,'Parity'))==2:
|
||||
parity=serial.PARITY_ODD
|
||||
bytesize=int(update.config().get(section,'WordSize'))
|
||||
dev=serial.Serial(tty_dev,speed,parity=parity,bytesize=bytesize)
|
||||
if(len(dps)!=0):
|
||||
dev.write(dps.encode('utf-8'))
|
||||
if(len(ps)!=0):
|
||||
dev.write(ps.encode('utf-8'))
|
||||
if(len(text)!=0):
|
||||
dev.write(text.encode('utf-8'))
|
||||
dev.close()
|
||||
|
||||
except configparser.NoOptionError:
|
||||
#
|
||||
# Use UDP output
|
||||
#
|
||||
ipaddr=update.config().get(section,'IpAddress')
|
||||
port=int(update.config().get(section,'UdpPort'))
|
||||
if(len(dps)!=0):
|
||||
send_sock.sendto(dps.encode('utf-8'),(ipaddr,port))
|
||||
if(len(ps)!=0):
|
||||
send_sock.sendto(ps.encode('utf-8'),(ipaddr,port))
|
||||
if(len(text)!=0):
|
||||
send_sock.sendto(text.encode('utf-8'),(ipaddr,port))
|
||||
n=n+1
|
||||
except configparser.NoSectionError:
|
||||
return
|
||||
|
||||
#
|
||||
# 'Main' function
|
||||
#
|
||||
# Create Send Socket
|
||||
#
|
||||
send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
|
||||
|
||||
rcvr=pypad.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_inno713.py: USAGE: cmd <hostname> <port> <config>')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
93
apis/pypad/scripts/pypad_liqcomp.exemplar
Normal file
93
apis/pypad/scripts/pypad_liqcomp.exemplar
Normal file
@@ -0,0 +1,93 @@
|
||||
; This is the configuration for the 'pypad_liqcomp.py' module for
|
||||
; Rivendell, which can be used to output Now & Next data to one or more
|
||||
; Liquid Compass Internet streaming encoders, using the following format:
|
||||
;
|
||||
; |<title>|<artist>|<cart-num>|<length>|<group>|<album>|<label>|<lf>
|
||||
|
||||
; Section Header
|
||||
;
|
||||
; One section per remote encoder is configured, starting with 'System1' and
|
||||
; working up consecutively
|
||||
[System1]
|
||||
|
||||
; IP Address
|
||||
;
|
||||
; The IP address of the remote encoder, in dotted-quad notation.
|
||||
IpAddress=192.168.10.22
|
||||
|
||||
; UDP Port
|
||||
;
|
||||
; The UDP port number of the remote encoder, in the range 0 - 65,535.
|
||||
UdpPort=5273
|
||||
|
||||
; Field Definitions. The string to use to populate the <title>, <artist>,
|
||||
; <album> and <label> fields sent to ANDO each time RDAirPlay changes play
|
||||
; state. These can include wildcards as placeholders for metadata values.
|
||||
; For the list of supported wildcards. see the 'Metadata Wildcards' section
|
||||
; of the Rivendell Operations Guide.
|
||||
Title=%t
|
||||
Artist=%a
|
||||
Album=%l
|
||||
Label=%b
|
||||
|
||||
; Log Selection
|
||||
;
|
||||
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
|
||||
; state changes on that log should be output on this udp port. If set
|
||||
; to 'Onair', then output will be generated only if the RDAirPlay OnAir flag
|
||||
; is active.
|
||||
MasterLog=Yes
|
||||
Aux1Log=Yes
|
||||
Aux2Log=Yes
|
||||
VLog101=No
|
||||
VLog102=No
|
||||
VLog103=No
|
||||
VLog104=No
|
||||
VLog105=No
|
||||
VLog106=No
|
||||
VLog107=No
|
||||
VLog108=No
|
||||
VLog109=No
|
||||
VLog110=No
|
||||
VLog111=No
|
||||
VLog112=No
|
||||
VLog113=No
|
||||
VLog114=No
|
||||
VLog115=No
|
||||
VLog116=No
|
||||
VLog117=No
|
||||
VLog118=No
|
||||
VLog119=No
|
||||
VLog120=No
|
||||
|
||||
|
||||
; Additional encoders can be configured by adding new sections...
|
||||
;[System2]
|
||||
;IpAddress=192.168.10.22
|
||||
;UdpPort=6789
|
||||
;Title=%u
|
||||
;Artist=%a
|
||||
;Album=%p
|
||||
;MasterLog=Yes
|
||||
;Aux1Log=No
|
||||
;Aux2Log=Onair
|
||||
;VLog101=No
|
||||
;VLog102=No
|
||||
;VLog103=No
|
||||
;VLog104=No
|
||||
;VLog105=No
|
||||
;VLog106=No
|
||||
;VLog107=No
|
||||
;VLog108=No
|
||||
;VLog109=No
|
||||
;VLog110=No
|
||||
;VLog111=No
|
||||
;VLog112=No
|
||||
;VLog113=No
|
||||
;VLog114=No
|
||||
;VLog115=No
|
||||
;VLog116=No
|
||||
;VLog117=No
|
||||
;VLog118=No
|
||||
;VLog119=No
|
||||
;VLog120=No
|
72
apis/pypad/scripts/pypad_liqcomp.py
Executable file
72
apis/pypad/scripts/pypad_liqcomp.py
Executable file
@@ -0,0 +1,72 @@
|
||||
#!%PYTHON_BANGPATH%
|
||||
|
||||
# pypad_liqcomp.py
|
||||
#
|
||||
# Send PAD updates to a Liquid Compass stream encoder
|
||||
#
|
||||
# (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 sys
|
||||
import socket
|
||||
import configparser
|
||||
import pypad
|
||||
|
||||
last_updates={}
|
||||
|
||||
def eprint(*args,**kwargs):
|
||||
print(*args,file=sys.stderr,**kwargs)
|
||||
|
||||
def ProcessPad(update):
|
||||
try:
|
||||
last_updates[update.machine()]
|
||||
except KeyError:
|
||||
last_updates[update.machine()]=None
|
||||
|
||||
n=1
|
||||
while(True):
|
||||
section='System'+str(n)
|
||||
try:
|
||||
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW) and (last_updates[update.machine()] != update.startDateTimeString(pypad.TYPE_NOW)):
|
||||
last_updates[update.machine()]=update.startDateTimeString(pypad.TYPE_NOW)
|
||||
title=update.resolvePadFields(update.config().get(section,'Title'),pypad.ESCAPE_NONE)
|
||||
artist=update.resolvePadFields(update.config().get(section,'Artist'),pypad.ESCAPE_NONE)
|
||||
album=update.resolvePadFields(update.config().get(section,'Album'),pypad.ESCAPE_NONE)
|
||||
label=update.resolvePadFields(update.config().get(section,'Label'),pypad.ESCAPE_NONE)
|
||||
secs=update.padField(pypad.TYPE_NOW,pypad.FIELD_LENGTH)
|
||||
group=update.padField(pypad.TYPE_NOW,pypad.FIELD_GROUP_NAME)
|
||||
msg='|'+title+'|'+artist+'|'+str(update.padField(pypad.TYPE_NOW,pypad.FIELD_CART_NUMBER))+'|'+str(secs)+'|'+group+'|'+album+'|'+label+'|\n'
|
||||
send_sock.sendto(msg.encode('utf-8'),
|
||||
(update.config().get(section,'IpAddress'),int(update.config().get(section,'UdpPort'))))
|
||||
n=n+1
|
||||
except configparser.NoSectionError:
|
||||
return
|
||||
|
||||
#
|
||||
# 'Main' function
|
||||
#
|
||||
# Create Send Socket
|
||||
#
|
||||
send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
|
||||
|
||||
rcvr=pypad.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_liqcomp.py: USAGE: cmd <hostname> <port> <config>')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
97
apis/pypad/scripts/pypad_live365.exemplar
Normal file
97
apis/pypad/scripts/pypad_live365.exemplar
Normal file
@@ -0,0 +1,97 @@
|
||||
; This is the configuration for the 'pypad_live365.py' script for
|
||||
; Rivendell, which can be used to update the metadata on a Live 365 channel
|
||||
; using Now & Next data.
|
||||
|
||||
; Section Header
|
||||
;
|
||||
; One section per Live365 station is configured, starting with
|
||||
; 'Station1' and working up consecutively
|
||||
[Station1]
|
||||
|
||||
|
||||
; MemberName
|
||||
;
|
||||
; The member name of the Live365 station to which to send updates.
|
||||
MemberName=my_station
|
||||
|
||||
|
||||
; Password
|
||||
;
|
||||
; The password of the Live365 station to which to send updates.
|
||||
Password=changeme
|
||||
|
||||
|
||||
; Metadata String. The metadata fields to be sent each time RDAirPlay changes
|
||||
; play state, including any wildcards as placeholders for metadata values.
|
||||
;
|
||||
; The list of available wildcards can be found in the 'metadata_wildcards.txt'
|
||||
; file in the Rivendell documentation directory.
|
||||
;
|
||||
TitleString=%t
|
||||
ArtistString=%a
|
||||
AlbumString=%l
|
||||
|
||||
|
||||
; Log Selection
|
||||
;
|
||||
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
|
||||
; state changes on that log should be output to this station. If set
|
||||
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
|
||||
; is active.
|
||||
MasterLog=Yes
|
||||
Aux1Log=Yes
|
||||
Aux2Log=Yes
|
||||
VLog101=No
|
||||
VLog102=No
|
||||
VLog103=No
|
||||
VLog104=No
|
||||
VLog105=No
|
||||
VLog106=No
|
||||
VLog107=No
|
||||
VLog108=No
|
||||
VLog109=No
|
||||
VLog110=No
|
||||
VLog111=No
|
||||
VLog112=No
|
||||
VLog113=No
|
||||
VLog114=No
|
||||
VLog115=No
|
||||
VLog116=No
|
||||
VLog117=No
|
||||
VLog118=No
|
||||
VLog119=No
|
||||
VLog120=No
|
||||
|
||||
|
||||
; Additional Live365 stations can be configured by adding new
|
||||
; sections...
|
||||
;
|
||||
;[Station2]
|
||||
;MemberName=your_station
|
||||
;Password=changeme
|
||||
;TitleString=%t
|
||||
;ArtistString=%a
|
||||
;AlbumString=%l
|
||||
;MasterLog=No
|
||||
;Aux1Log=Yes
|
||||
;Aux2Log=No
|
||||
;VLog101=No
|
||||
;VLog102=No
|
||||
;VLog103=No
|
||||
;VLog104=No
|
||||
;VLog105=No
|
||||
;VLog106=No
|
||||
;VLog107=No
|
||||
;VLog108=No
|
||||
;VLog109=No
|
||||
;VLog110=No
|
||||
;VLog111=No
|
||||
;VLog112=No
|
||||
;VLog113=No
|
||||
;VLog114=No
|
||||
;VLog115=No
|
||||
;VLog116=No
|
||||
;VLog117=No
|
||||
;VLog118=No
|
||||
;VLog119=No
|
||||
;VLog120=No
|
76
apis/pypad/scripts/pypad_live365.py
Executable file
76
apis/pypad/scripts/pypad_live365.py
Executable file
@@ -0,0 +1,76 @@
|
||||
#!%PYTHON_BANGPATH%
|
||||
|
||||
# pypad_live365.py
|
||||
#
|
||||
# Write PAD updates to Live365 stations
|
||||
#
|
||||
# (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 sys
|
||||
import syslog
|
||||
import configparser
|
||||
import pycurl
|
||||
import pypad
|
||||
from io import BytesIO
|
||||
|
||||
def eprint(*args,**kwargs):
|
||||
print(*args,file=sys.stderr,**kwargs)
|
||||
|
||||
def ProcessPad(update):
|
||||
n=1
|
||||
try:
|
||||
while(True):
|
||||
section='Station'+str(n)
|
||||
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW):
|
||||
member=update.escape(update.config().get(section,'MemberName'),pypad.ESCAPE_URL)
|
||||
password=update.escape(update.config().get(section,'Password'),pypad.ESCAPE_URL)
|
||||
title=update.resolvePadFields(update.config().get(section,'TitleString'),pypad.ESCAPE_URL)
|
||||
artist=update.resolvePadFields(update.config().get(section,'ArtistString'),pypad.ESCAPE_URL)
|
||||
album=update.resolvePadFields(update.config().get(section,'AlbumString'),pypad.ESCAPE_URL)
|
||||
seconds=str(update.padField(pypad.TYPE_NOW,pypad.FIELD_LENGTH)//1000)
|
||||
buf=BytesIO()
|
||||
curl=pycurl.Curl()
|
||||
url='http://www.live365.com/cgi-bin/add_song.cgi?member_name='+member+'&password='+password+'&version=2&filename=Rivendell&seconds='+seconds+'&title='+title+'&artist='+artist+'&album='+album
|
||||
curl.setopt(curl.URL,url)
|
||||
curl.setopt(curl.WRITEDATA,buf)
|
||||
curl.setopt(curl.FOLLOWLOCATION,True)
|
||||
try:
|
||||
curl.perform()
|
||||
code=curl.getinfo(pycurl.RESPONSE_CODE)
|
||||
if (code<200) or (code>=300):
|
||||
syslog.syslog(syslog.LOG_WARNING,'['+section+'] returned response code '+str(code))
|
||||
except pycurl.error:
|
||||
syslog.syslog(syslog.LOG_WARNING,'['+section+'] failed: '+curl.errstr())
|
||||
curl.close()
|
||||
n=n+1
|
||||
|
||||
except configparser.NoSectionError:
|
||||
return
|
||||
|
||||
#
|
||||
# 'Main' function
|
||||
#
|
||||
syslog.openlog(sys.argv[0].split('/')[-1])
|
||||
|
||||
rcvr=pypad.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_live365.py: USAGE: cmd <hostname> <port> <config>')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
116
apis/pypad/scripts/pypad_serial.exemplar
Normal file
116
apis/pypad/scripts/pypad_serial.exemplar
Normal file
@@ -0,0 +1,116 @@
|
||||
; This is the configuration for the 'pypad_serial.py' script for
|
||||
; Rivendell, which can be used to output Now & Next data via one or more
|
||||
; serial ports.
|
||||
;
|
||||
; NOTE: The serial ports configured here have NOTHING TO DO with the
|
||||
; ports configured in RDAdmin! These ports are used strictly by the
|
||||
; 'pypad_serial.py' plugin, and will not be usable by any other Rivendell
|
||||
; component.
|
||||
|
||||
; Section Header
|
||||
;
|
||||
; One per serial device to be configured, starting with 'Serial1' and
|
||||
; working up consecutively
|
||||
[Serial1]
|
||||
|
||||
; Serial Device
|
||||
;
|
||||
; The device file that corresponds to the serial device.
|
||||
Device=/dev/ttyS0
|
||||
|
||||
; Serial Baud Rate (in bps)
|
||||
Speed=9600
|
||||
|
||||
; Parity (0=none, 1=even, 2=odd)
|
||||
Parity=0
|
||||
|
||||
; Number of bits per data 'word'.
|
||||
WordSize=8
|
||||
|
||||
; Format String. The string to be output each time RDAirPlay changes
|
||||
; play state, including any wildcards as placeholders for metadata values.
|
||||
;
|
||||
; The list of available wildcards can be found in the 'metadata_wildcards.txt'
|
||||
; file in the Rivendell documentation directory.
|
||||
;
|
||||
FormatString=NOW: %d(ddd MMM d hh:mm:ss yyyy): %t - %a\r\nNEXT: %D(ddd MMM d hh:mm:ss yyyy): %T - %A\r\n
|
||||
|
||||
; Encoding. Defines the set of escapes to be applied to the PAD fields.
|
||||
; The following options are available:
|
||||
;
|
||||
; 0 - Perform no character escaping.
|
||||
; 1 - "XML" escaping: Escape reserved characters as per XML-v1.0
|
||||
; 2 - "Web" escaping: Escape reserved characters as per RFC 2396 Section 2.4
|
||||
Encoding=0
|
||||
|
||||
; Null Update Handling. Defines how 'null' updates --i.e. those with a cart
|
||||
; number of '0' -- should be handled.
|
||||
;
|
||||
; 0 - Process all updates regardless of cart values.
|
||||
; 1 - Process update only if the 'now' cart is not null.
|
||||
; 2 - Process update only if the 'next' cart is not null.
|
||||
; 3 - Process update only if both the 'now' and 'next' carts are not null.
|
||||
ProcessNullUpdates=0
|
||||
|
||||
; Log Selection
|
||||
;
|
||||
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
|
||||
; state changes on that log should be output on this serial port. If set
|
||||
; to 'Onair', then output will be generated only if RDAirPlay's OnAir flag
|
||||
; is active.
|
||||
MasterLog=Yes
|
||||
Aux1Log=Yes
|
||||
Aux2Log=Yes
|
||||
VLog101=No
|
||||
VLog102=No
|
||||
VLog103=No
|
||||
VLog104=No
|
||||
VLog105=No
|
||||
VLog106=No
|
||||
VLog107=No
|
||||
VLog108=No
|
||||
VLog109=No
|
||||
VLog110=No
|
||||
VLog111=No
|
||||
VLog112=No
|
||||
VLog113=No
|
||||
VLog114=No
|
||||
VLog115=No
|
||||
VLog116=No
|
||||
VLog117=No
|
||||
VLog118=No
|
||||
VLog119=No
|
||||
VLog120=No
|
||||
|
||||
|
||||
; Additional serial ports can be configured by adding new sections...
|
||||
;[Serial2]
|
||||
;Device=/dev/ttyS1
|
||||
;Speed=9600
|
||||
;Parity=0
|
||||
;WordSize=8
|
||||
;FormatString=%t
|
||||
;ProcessNullUpdates=0
|
||||
;MasterLog=Yes
|
||||
;Aux1Log=No
|
||||
;Aux2Log=Onair
|
||||
;VLog101=No
|
||||
;VLog102=No
|
||||
;VLog103=No
|
||||
;VLog104=No
|
||||
;VLog105=No
|
||||
;VLog106=No
|
||||
;VLog107=No
|
||||
;VLog108=No
|
||||
;VLog109=No
|
||||
;VLog110=No
|
||||
;VLog111=No
|
||||
;VLog112=No
|
||||
;VLog113=No
|
||||
;VLog114=No
|
||||
;VLog115=No
|
||||
;VLog116=No
|
||||
;VLog117=No
|
||||
;VLog118=No
|
||||
;VLog119=No
|
||||
;VLog120=No
|
69
apis/pypad/scripts/pypad_serial.py
Executable file
69
apis/pypad/scripts/pypad_serial.py
Executable file
@@ -0,0 +1,69 @@
|
||||
#!%PYTHON_BANGPATH%
|
||||
|
||||
# pypad_serial.py
|
||||
#
|
||||
# Write PAD updates to arbitrary URLs
|
||||
#
|
||||
# (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 sys
|
||||
import syslog
|
||||
import configparser
|
||||
import pypad
|
||||
import serial
|
||||
from io import BytesIO
|
||||
|
||||
def eprint(*args,**kwargs):
|
||||
print(*args,file=sys.stderr,**kwargs)
|
||||
|
||||
def ProcessPad(update):
|
||||
n=1
|
||||
try:
|
||||
while(True):
|
||||
section='Serial'+str(n)
|
||||
if update.shouldBeProcessed(section):
|
||||
devname=update.config().get(section,'Device')
|
||||
speed=int(update.config().get(section,'Speed'))
|
||||
parity=serial.PARITY_NONE
|
||||
if int(update.config().get(section,'Parity'))==1:
|
||||
parity=serial.PARITY_EVEN
|
||||
if int(update.config().get(section,'Parity'))==2:
|
||||
parity=serial.PARITY_ODD
|
||||
bytesize=int(update.config().get(section,'WordSize'))
|
||||
dev=serial.Serial(devname,speed,parity=parity,bytesize=bytesize)
|
||||
fmtstr=update.config().get(section,'FormatString')
|
||||
esc=int(update.config().get(section,'Encoding'))
|
||||
dev.write(update.resolvePadFields(fmtstr,esc).encode('utf-8'))
|
||||
dev.close()
|
||||
n=n+1
|
||||
|
||||
except configparser.NoSectionError:
|
||||
return
|
||||
|
||||
#
|
||||
# 'Main' function
|
||||
#
|
||||
syslog.openlog(sys.argv[0].split('/')[-1])
|
||||
|
||||
rcvr=pypad.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_serial.py: USAGE: cmd <hostname> <port> <config>')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
96
apis/pypad/scripts/pypad_shoutcast1.exemplar
Normal file
96
apis/pypad/scripts/pypad_shoutcast1.exemplar
Normal file
@@ -0,0 +1,96 @@
|
||||
; This is the sample configuration for the 'pypad_shoutcast1.py' script for
|
||||
; Rivendell, which can be used to update the metadata on an Shoutcast 1.x
|
||||
; server using Now & Next data.
|
||||
|
||||
|
||||
; Section Header
|
||||
;
|
||||
; One section per Shoutcast server instance is configured, starting with
|
||||
; 'Shoutcast1' and working up consecutively
|
||||
[Shoutcast1]
|
||||
|
||||
; Password
|
||||
;
|
||||
; The password of the Shoutcast server instance to which to send updates.
|
||||
Password=changeme
|
||||
|
||||
; Host Name
|
||||
;
|
||||
; The fully-qualified domain name or IP address of the Shoutcast server
|
||||
Hostname=shoutcast.example.com
|
||||
|
||||
; Host Port
|
||||
;
|
||||
; The TCP port number of the Shoutcast server
|
||||
Tcpport=8000
|
||||
|
||||
; Format String. The metadata to be sent each time RDAirPlay changes
|
||||
; play state, including any wildcards as placeholders for metadata values.
|
||||
;
|
||||
; The list of available wildcards can be found in the 'metadata_wildcards.txt'
|
||||
; file in the Rivendell documentation directory.
|
||||
;
|
||||
FormatString=%a%20-%20%t
|
||||
|
||||
; Log Selection
|
||||
;
|
||||
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
|
||||
; state changes on that log should be output to this account. If set
|
||||
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
|
||||
; is active.
|
||||
MasterLog=Yes
|
||||
Aux1Log=Yes
|
||||
Aux2Log=Yes
|
||||
VLog101=No
|
||||
VLog102=No
|
||||
VLog103=No
|
||||
VLog104=No
|
||||
VLog105=No
|
||||
VLog106=No
|
||||
VLog107=No
|
||||
VLog108=No
|
||||
VLog109=No
|
||||
VLog110=No
|
||||
VLog111=No
|
||||
VLog112=No
|
||||
VLog113=No
|
||||
VLog114=No
|
||||
VLog115=No
|
||||
VLog116=No
|
||||
VLog117=No
|
||||
VLog118=No
|
||||
VLog119=No
|
||||
VLog120=No
|
||||
|
||||
|
||||
; Additional Shoutcast server instances can be configured by adding new
|
||||
; sections...
|
||||
;
|
||||
;[Shoutcast2]
|
||||
;Password=letmein
|
||||
;Hostname=anotherone.example.com
|
||||
;Tcpport=80
|
||||
;FormatString=%t by %a
|
||||
;MasterLog=Yes
|
||||
;Aux1Log=No
|
||||
;Aux2Log=Onair
|
||||
;VLog101=No
|
||||
;VLog102=No
|
||||
;VLog103=No
|
||||
;VLog104=No
|
||||
;VLog105=No
|
||||
;VLog106=No
|
||||
;VLog107=No
|
||||
;VLog108=No
|
||||
;VLog109=No
|
||||
;VLog110=No
|
||||
;VLog111=No
|
||||
;VLog112=No
|
||||
;VLog113=No
|
||||
;VLog114=No
|
||||
;VLog115=No
|
||||
;VLog116=No
|
||||
;VLog117=No
|
||||
;VLog118=No
|
||||
;VLog119=No
|
||||
;VLog120=No
|
83
apis/pypad/scripts/pypad_shoutcast1.py
Executable file
83
apis/pypad/scripts/pypad_shoutcast1.py
Executable file
@@ -0,0 +1,83 @@
|
||||
#!%PYTHON_BANGPATH%
|
||||
|
||||
# pypad_shoutcast1.py
|
||||
#
|
||||
# Write PAD updates to a Shoutcast 1 instance
|
||||
#
|
||||
# (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 sys
|
||||
import syslog
|
||||
import configparser
|
||||
import pycurl
|
||||
import pypad
|
||||
from io import BytesIO
|
||||
|
||||
last_updates={}
|
||||
|
||||
def eprint(*args,**kwargs):
|
||||
print(*args,file=sys.stderr,**kwargs)
|
||||
|
||||
def ProcessPad(update):
|
||||
try:
|
||||
last_updates[update.machine()]
|
||||
except KeyError:
|
||||
last_updates[update.machine()]=None
|
||||
|
||||
n=1
|
||||
try:
|
||||
while(True):
|
||||
section='Shoutcast'+str(n)
|
||||
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW) and (last_updates[update.machine()] != update.startDateTimeString(pypad.TYPE_NOW)):
|
||||
last_updates[update.machine()]=update.startDateTimeString(pypad.TYPE_NOW)
|
||||
song=update.resolvePadFields(update.config().get(section,'FormatString'),pypad.ESCAPE_URL)
|
||||
url='http://'+update.config().get(section,'Hostname')+':'+str(update.config().get(section,'Tcpport'))+'/admin.cgi?pass='+update.escape(update.config().get(section,'Password'),pypad.ESCAPE_URL)+'&mode=updinfo&song='+song
|
||||
curl=pycurl.Curl()
|
||||
curl.setopt(curl.URL,url)
|
||||
headers=[]
|
||||
#
|
||||
# D.N.A.S v1.9.8 refuses to process updates with the default
|
||||
# CURL user-agent value, hence we lie to it.
|
||||
#
|
||||
headers.append('User-Agent: '+'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2) Gecko/20070219 Firefox/2.0.0.2')
|
||||
curl.setopt(curl.HTTPHEADER,headers);
|
||||
try:
|
||||
curl.perform()
|
||||
code=curl.getinfo(pycurl.RESPONSE_CODE)
|
||||
if (code<200) or (code>=300):
|
||||
syslog.syslog(syslog.LOG_WARNING,'['+section+'] returned response code '+str(code))
|
||||
except pycurl.error:
|
||||
syslog.syslog(syslog.LOG_WARNING,'['+section+'] failed: '+curl.errstr())
|
||||
curl.close()
|
||||
n=n+1
|
||||
|
||||
except configparser.NoSectionError:
|
||||
return
|
||||
|
||||
#
|
||||
# 'Main' function
|
||||
#
|
||||
syslog.openlog(sys.argv[0].split('/')[-1])
|
||||
|
||||
rcvr=pypad.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_shoutcast1.py: USAGE: cmd <hostname> <port> <config>')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
132
apis/pypad/scripts/pypad_spinitron.exemplar
Normal file
132
apis/pypad/scripts/pypad_spinitron.exemplar
Normal file
@@ -0,0 +1,132 @@
|
||||
; This is the configuration for the 'pypad_spinitron' script
|
||||
; for Rivendell, which can be used log Now & Next data to the Spinitron
|
||||
; online playlist service [http://www.spinitron.com].
|
||||
|
||||
; Section Header
|
||||
;
|
||||
; One section per Spinitron account is configured, starting with
|
||||
; 'Spinitron1' and working up consecutively
|
||||
[Spinitron1]
|
||||
|
||||
; APIKey
|
||||
;
|
||||
; API key for the Spinitron v2 account to which to log the play-out.
|
||||
; (This setting is only needed for Spinitron major version 2).
|
||||
APIKey=change_me_please
|
||||
|
||||
; PlaylistMode
|
||||
;
|
||||
; Set the Spinitron playlist mode to use when sending updates.
|
||||
;
|
||||
; (For a discussion of the implications of this setting on your
|
||||
; Spinitron playlists, see Section 3.2 of the 'Spinitron Automation
|
||||
; Integration' document, available from Spinitron).
|
||||
;
|
||||
; The following options are recognized:
|
||||
;
|
||||
; Full - Always use the Spinitron 'Full Automation' mode.
|
||||
; Assist - Always use the Spinitron 'Live Assist' mode.
|
||||
; Follow - Use the Spinitron 'Full Automation' mode when RDAirPlay
|
||||
; is in Automatic, otherwise use the Spinitron 'Live Assist' mode.
|
||||
PlaylistMode=Follow
|
||||
|
||||
; Title. The string to be sent as the 'Title' field for each update,
|
||||
; including any wildcards as placeholders for metadata values. For the
|
||||
; list of supported wildcards. see the 'Metadata Wildcards' section of the
|
||||
; Rivendell Operations Guide.
|
||||
Title=%t
|
||||
|
||||
; Artist. The string to be sent as the 'Artist' field for each update,
|
||||
; including any wildcards as placeholders for metadata values.
|
||||
Artist=%a
|
||||
|
||||
; Album. The string to be sent as the 'Album' field for each update,
|
||||
; including any wildcards as placeholders for metadata values.
|
||||
Album=%l
|
||||
|
||||
; Label. The string to be sent as the 'Label' field for each update,
|
||||
; including any wildcards as placeholders for metadata values.
|
||||
Label=%b
|
||||
|
||||
; Composer. The string to be sent as the 'Composer' field for each update,
|
||||
; including any wildcards as placeholders for metadata values.
|
||||
Composer=%m
|
||||
|
||||
; Conductor. The string to be sent as the 'Conductor' field for each update,
|
||||
; including any wildcards as placeholders for metadata values.
|
||||
Conductor=%r
|
||||
|
||||
; Notes. The string to be sent as the 'Notes' field for each update,
|
||||
; including any wildcards as placeholders for metadata values.
|
||||
Notes=%u
|
||||
|
||||
; Log Selection
|
||||
;
|
||||
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
|
||||
; state changes on that log should be output to this account. If set
|
||||
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
|
||||
; is active.
|
||||
MasterLog=Yes
|
||||
Aux1Log=Yes
|
||||
Aux2Log=Yes
|
||||
VLog101=No
|
||||
VLog102=No
|
||||
VLog103=No
|
||||
VLog104=No
|
||||
VLog105=No
|
||||
VLog106=No
|
||||
VLog107=No
|
||||
VLog108=No
|
||||
VLog109=No
|
||||
VLog110=No
|
||||
VLog111=No
|
||||
VLog112=No
|
||||
VLog113=No
|
||||
VLog114=No
|
||||
VLog115=No
|
||||
VLog116=No
|
||||
VLog117=No
|
||||
VLog118=No
|
||||
VLog119=No
|
||||
VLog120=No
|
||||
|
||||
|
||||
; Additional Spinitron instances can be configured by adding new
|
||||
; sections...
|
||||
;
|
||||
;[Spinitron2]
|
||||
;MajorVersion=2
|
||||
;Station=abcd
|
||||
;Username=metoo
|
||||
;Password=letmein
|
||||
;APIKey=some_different_key
|
||||
;Title=%t
|
||||
;Artist=%a
|
||||
;Album=%l
|
||||
;Label=%b
|
||||
;Composer=%m
|
||||
;Conductor=%r
|
||||
;Notes=%u
|
||||
;MasterLog=Yes
|
||||
;Aux1Log=No
|
||||
;Aux2Log=Onair
|
||||
;VLog101=No
|
||||
;VLog102=No
|
||||
;VLog103=No
|
||||
;VLog104=No
|
||||
;VLog105=No
|
||||
;VLog106=No
|
||||
;VLog107=No
|
||||
;VLog108=No
|
||||
;VLog109=No
|
||||
;VLog110=No
|
||||
;VLog111=No
|
||||
;VLog112=No
|
||||
;VLog113=No
|
||||
;VLog114=No
|
||||
;VLog115=No
|
||||
;VLog116=No
|
||||
;VLog117=No
|
||||
;VLog118=No
|
||||
;VLog119=No
|
||||
;VLog120=No
|
128
apis/pypad/scripts/pypad_spinitron.py
Executable file
128
apis/pypad/scripts/pypad_spinitron.py
Executable file
@@ -0,0 +1,128 @@
|
||||
#!%PYTHON_BANGPATH%
|
||||
|
||||
# pypad_spinitron.py
|
||||
#
|
||||
# Write PAD updates to the Spinitron Playlist Service
|
||||
#
|
||||
# (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 sys
|
||||
import syslog
|
||||
import configparser
|
||||
import pycurl
|
||||
import pypad
|
||||
from io import BytesIO
|
||||
|
||||
last_updates={}
|
||||
|
||||
def eprint(*args,**kwargs):
|
||||
print(*args,file=sys.stderr,**kwargs)
|
||||
|
||||
def JsonField(update,tag,value,is_last=False):
|
||||
if (value==None) or (value==''):
|
||||
ret=' "'+tag+'": null'
|
||||
else:
|
||||
ret=' "'+tag+'": "'+update.escape(value,pypad.ESCAPE_JSON)+'"'
|
||||
if not is_last:
|
||||
ret+=','
|
||||
return ret+'\r\n'
|
||||
|
||||
def ProcessPad(update):
|
||||
try:
|
||||
last_updates[update.machine()]
|
||||
except KeyError:
|
||||
last_updates[update.machine()]=None
|
||||
|
||||
n=1
|
||||
try:
|
||||
while(True):
|
||||
section='Spinitron'+str(n)
|
||||
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW) and (last_updates[update.machine()] != update.startDateTimeString(pypad.TYPE_NOW)):
|
||||
last_updates[update.machine()]=update.startDateTimeString(pypad.TYPE_NOW)
|
||||
title=update.resolvePadFields(update.config().get(section,'Title'),pypad.ESCAPE_JSON)
|
||||
artist=update.resolvePadFields(update.config().get(section,'Artist'),pypad.ESCAPE_JSON)
|
||||
album=update.resolvePadFields(update.config().get(section,'Album'),pypad.ESCAPE_JSON)
|
||||
label=update.resolvePadFields(update.config().get(section,'Label'),pypad.ESCAPE_JSON)
|
||||
composer=update.resolvePadFields(update.config().get(section,'Composer'),pypad.ESCAPE_JSON)
|
||||
conductor=update.resolvePadFields(update.config().get(section,'Conductor'),pypad.ESCAPE_JSON)
|
||||
notes=update.resolvePadFields(update.config().get(section,'Notes'),pypad.ESCAPE_JSON)
|
||||
|
||||
json='{\r\n'
|
||||
pmode=update.config().get(section,'PlaylistMode')
|
||||
if pmode=='Full':
|
||||
json+=' "live": false\r\n'
|
||||
if pmode=='Assist':
|
||||
json+=' "live:" true\r\n'
|
||||
if pmode=='Follow':
|
||||
if update.mode()=='Automatic':
|
||||
json+=' "live": false,\r\n'
|
||||
else:
|
||||
json+=' "live": true,\r\n'
|
||||
duration=str(update.padField(pypad.TYPE_NOW,pypad.FIELD_LENGTH)//1000)
|
||||
year=update.padField(pypad.TYPE_NOW,pypad.FIELD_YEAR)
|
||||
if year==None:
|
||||
json+=' "released": null,\r\n'
|
||||
else:
|
||||
json+=' "released": '+str(year)+',\r\n'
|
||||
json+=' "duration": '+duration+',\r\n'
|
||||
json+=JsonField(update,'artist',artist)
|
||||
json+=JsonField(update,'release',album)
|
||||
json+=JsonField(update,'label',label)
|
||||
json+=JsonField(update,'song',title)
|
||||
json+=JsonField(update,'composer',composer)
|
||||
json+=JsonField(update,'conductor',conductor)
|
||||
json+=JsonField(update,'note',notes)
|
||||
json+=JsonField(update,'isrc',update.padField(pypad.TYPE_NOW,pypad.FIELD_ISRC),True)
|
||||
json+='}\r\n'
|
||||
send_buf=BytesIO(json.encode('utf-8'))
|
||||
recv_buf=BytesIO()
|
||||
curl=pycurl.Curl()
|
||||
curl.setopt(curl.URL,'https://spinitron.com/api/spins')
|
||||
headers=[]
|
||||
headers.append('Authorization: Bearer '+update.config().get(section,'APIKey'))
|
||||
headers.append('Content-Type: application/json')
|
||||
headers.append('Content-Length: '+str(len(json.encode('utf-8'))))
|
||||
curl.setopt(curl.HTTPHEADER,headers);
|
||||
curl.setopt(curl.POST,True)
|
||||
curl.setopt(curl.READDATA,send_buf)
|
||||
curl.setopt(curl.WRITEDATA,recv_buf)
|
||||
try:
|
||||
curl.perform()
|
||||
code=curl.getinfo(pycurl.RESPONSE_CODE)
|
||||
if (code<200) or (code>=300):
|
||||
syslog.syslog(syslog.LOG_WARNING,'['+section+'] returned response code '+str(code))
|
||||
except pycurl.error:
|
||||
syslog.syslog(syslog.LOG_WARNING,'['+section+'] failed: '+curl.errstr())
|
||||
curl.close()
|
||||
n=n+1
|
||||
|
||||
except configparser.NoSectionError:
|
||||
return
|
||||
|
||||
#
|
||||
# 'Main' function
|
||||
#
|
||||
syslog.openlog(sys.argv[0].split('/')[-1])
|
||||
|
||||
rcvr=pypad.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_spinitron.py: USAGE: cmd <hostname> <port> <config>')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
123
apis/pypad/scripts/pypad_spottrap.exemplar
Normal file
123
apis/pypad/scripts/pypad_spottrap.exemplar
Normal file
@@ -0,0 +1,123 @@
|
||||
; This is the configuration for the 'pypad_spottrap.script' script for
|
||||
; Rivendell, which can be used to output Now & Next data to one or more
|
||||
; remote UDP ports on the basis of Group and Length.
|
||||
;
|
||||
; Module configuration consists of a list of rules, each of which consists
|
||||
; in turn with a set of filter parameters and actions. Each PAD update is
|
||||
; compared against the filter parameters of each rule. If the filter matches,
|
||||
; then the rule actions are performed.
|
||||
;
|
||||
|
||||
; Rule Header
|
||||
;
|
||||
; One section per rule is configured, starting with 'Rule1' and
|
||||
; working up consecutively
|
||||
[Rule1]
|
||||
|
||||
; FILTER PARAMETERS
|
||||
;
|
||||
; Group Name
|
||||
;
|
||||
; The name of the Rivendell group to match.
|
||||
GroupName=BEDS
|
||||
|
||||
; Minimum Length
|
||||
;
|
||||
; The minimum length, in milliseconds. A PAD update with a Length of less
|
||||
; than this value will not be matched.
|
||||
MinimumLength=0
|
||||
|
||||
; Maximum Length
|
||||
; The maximum length, in milliseconds. A PAD update with a Length of more
|
||||
; than this value will not be matched.
|
||||
MaximumLength=10000000
|
||||
|
||||
; ACTION PARAMETERS
|
||||
;
|
||||
; IP Address
|
||||
;
|
||||
; The IP address of the remote UDP port, in dotted-quad notation.
|
||||
IpAddress=192.168.10.30
|
||||
|
||||
; UDP Port
|
||||
;
|
||||
; The UDP port number of the remote UDP port, in the range 0 - 65,535.
|
||||
UdpPort=5859
|
||||
|
||||
; Format String. The string to be output each time RDAirPlay changes
|
||||
; play state, including any wildcards as placeholders for metadata values.
|
||||
;
|
||||
; The list of available wildcards can be found in the 'metadata_wildcards.txt'
|
||||
; file in the Rivendell documentation directory.
|
||||
;
|
||||
FormatString=DX 1 %h!
|
||||
|
||||
; Default Format String. Similar to the 'FormatString=' parameter described
|
||||
; above, but this string is output when the rule *doesn't* match. All of
|
||||
; the wildcards described in the 'FormatString=' section apply here as well.
|
||||
DefaultFormatString=DX 1 0!
|
||||
|
||||
|
||||
; Log Selection
|
||||
;
|
||||
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
|
||||
; state changes on that log should be output on this udp port. If set
|
||||
; to 'Onair', then output will be generated only if RDAirPlay's OnAir flag
|
||||
; is active.
|
||||
MasterLog=Yes
|
||||
Aux1Log=Yes
|
||||
Aux2Log=Yes
|
||||
VLog101=No
|
||||
VLog102=No
|
||||
VLog103=No
|
||||
VLog104=No
|
||||
VLog105=No
|
||||
VLog106=No
|
||||
VLog107=No
|
||||
VLog108=No
|
||||
VLog109=No
|
||||
VLog110=No
|
||||
VLog111=No
|
||||
VLog112=No
|
||||
VLog113=No
|
||||
VLog114=No
|
||||
VLog115=No
|
||||
VLog116=No
|
||||
VLog117=No
|
||||
VLog118=No
|
||||
VLog119=No
|
||||
VLog120=No
|
||||
|
||||
|
||||
; Additional Rules can be configured by adding new sections...
|
||||
;[Rule2]
|
||||
;GroupName=TEST
|
||||
;MinimumLength=0
|
||||
;MaximumLength=1000000
|
||||
;FormatString=Artist: %a%r
|
||||
;DefaultFormatString=Artist: %a%r
|
||||
;IpAddress=192.168.10.30
|
||||
;UdpPort=6789
|
||||
;MasterLog=Yes
|
||||
;Aux1Log=No
|
||||
;Aux2Log=Onair
|
||||
;VLog101=No
|
||||
;VLog102=No
|
||||
;VLog103=No
|
||||
;VLog104=No
|
||||
;VLog105=No
|
||||
;VLog106=No
|
||||
;VLog107=No
|
||||
;VLog108=No
|
||||
;VLog109=No
|
||||
;VLog110=No
|
||||
;VLog111=No
|
||||
;VLog112=No
|
||||
;VLog113=No
|
||||
;VLog114=No
|
||||
;VLog115=No
|
||||
;VLog116=No
|
||||
;VLog117=No
|
||||
;VLog118=No
|
||||
;VLog119=No
|
||||
;VLog120=No
|
71
apis/pypad/scripts/pypad_spottrap.py
Executable file
71
apis/pypad/scripts/pypad_spottrap.py
Executable file
@@ -0,0 +1,71 @@
|
||||
#!%PYTHON_BANGPATH%
|
||||
|
||||
# pypad_spottrap.py
|
||||
#
|
||||
# Output Now & Next data on the basis of Group and Length.
|
||||
#
|
||||
# (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 sys
|
||||
import socket
|
||||
import configparser
|
||||
import pypad
|
||||
|
||||
last_updates={}
|
||||
|
||||
def eprint(*args,**kwargs):
|
||||
print(*args,file=sys.stderr,**kwargs)
|
||||
|
||||
|
||||
def ProcessPad(update):
|
||||
try:
|
||||
last_updates[update.machine()]
|
||||
except KeyError:
|
||||
last_updates[update.machine()]=None
|
||||
|
||||
n=1
|
||||
while(True):
|
||||
section='Rule'+str(n)
|
||||
try:
|
||||
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW) and (last_updates[update.machine()] != update.startDateTimeString(pypad.TYPE_NOW)):
|
||||
last_updates[update.machine()]=update.startDateTimeString(pypad.TYPE_NOW)
|
||||
length=update.padField(pypad.TYPE_NOW,pypad.FIELD_LENGTH)
|
||||
if update.padField(pypad.TYPE_NOW,pypad.FIELD_GROUP_NAME)==update.config().get(section,'GroupName') and length>=int(update.config().get(section,'MinimumLength')) and length<=int(update.config().get(section,'MaximumLength')):
|
||||
msg=update.resolvePadFields(update.config().get(section,'FormatString'),pypad.ESCAPE_NONE)
|
||||
else:
|
||||
msg=update.resolvePadFields(update.config().get(section,'DefaultFormatString'),pypad.ESCAPE_NONE)
|
||||
send_sock.sendto(msg.encode('utf-8'),
|
||||
(update.config().get(section,'IpAddress'),int(update.config().get(section,'UdpPort'))))
|
||||
n=n+1
|
||||
except configparser.NoSectionError:
|
||||
return
|
||||
|
||||
#
|
||||
# 'Main' function
|
||||
#
|
||||
# Create Send Socket
|
||||
#
|
||||
send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
|
||||
|
||||
rcvr=pypad.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_spottrap.py: USAGE: cmd <hostname> <port> <config>')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
105
apis/pypad/scripts/pypad_tunein.exemplar
Normal file
105
apis/pypad/scripts/pypad_tunein.exemplar
Normal file
@@ -0,0 +1,105 @@
|
||||
; This is the sample configuration for the 'pypad_tunein.py' PyPAD script for
|
||||
; Rivendell, which can be used to update the metadata at TuneIn server
|
||||
; using Now & Next data.
|
||||
;
|
||||
|
||||
; Section Header
|
||||
;
|
||||
; One section per TuneIn station is configured, starting with
|
||||
; 'Station1' and working up consecutively
|
||||
[Station1]
|
||||
|
||||
|
||||
; StationID
|
||||
;
|
||||
; The Station ID of the TuneIn station to which to send updates.
|
||||
StationID=s123456
|
||||
|
||||
|
||||
; PartnerID
|
||||
;
|
||||
; The Partner ID of the TuneIn station to which to send updates.
|
||||
PartnerID=changeme
|
||||
|
||||
|
||||
; PartnerKey
|
||||
;
|
||||
; The Partner Key of the TuneIn station to which to send updates.
|
||||
PartnerKey=changeme
|
||||
|
||||
|
||||
; Metadata String. The metadata fields to be sent each time RDAirPlay changes
|
||||
; play state, including any wildcards as placeholders for metadata values.
|
||||
;
|
||||
; The list of available wildcards can be found in the 'metadata_wildcards.txt'
|
||||
; file in the Rivendell documentation directory.
|
||||
;
|
||||
TitleString=%t
|
||||
ArtistString=%a
|
||||
AlbumString=%l
|
||||
|
||||
|
||||
; Log Selection
|
||||
;
|
||||
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
|
||||
; state changes on that log should be output to this station. If set
|
||||
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
|
||||
; is active.
|
||||
MasterLog=Yes
|
||||
Aux1Log=Yes
|
||||
Aux2Log=Yes
|
||||
VLog101=No
|
||||
VLog102=No
|
||||
VLog103=No
|
||||
VLog104=No
|
||||
VLog105=No
|
||||
VLog106=No
|
||||
VLog107=No
|
||||
VLog108=No
|
||||
VLog109=No
|
||||
VLog110=No
|
||||
VLog111=No
|
||||
VLog112=No
|
||||
VLog113=No
|
||||
VLog114=No
|
||||
VLog115=No
|
||||
VLog116=No
|
||||
VLog117=No
|
||||
VLog118=No
|
||||
VLog119=No
|
||||
VLog120=No
|
||||
|
||||
|
||||
; Additional TuneIn stations can be configured by adding new
|
||||
; sections...
|
||||
;
|
||||
;[Station2]
|
||||
;StationID=s654321
|
||||
;PartnerID=changeme
|
||||
;PartnerKey=changeme
|
||||
;TitleString=%t
|
||||
;ArtistString=%a
|
||||
;AlbumString=%l
|
||||
;MasterLog=No
|
||||
;Aux1Log=Yes
|
||||
;Aux2Log=No
|
||||
;VLog101=No
|
||||
;VLog102=No
|
||||
;VLog103=No
|
||||
;VLog104=No
|
||||
;VLog105=No
|
||||
;VLog106=No
|
||||
;VLog107=No
|
||||
;VLog108=No
|
||||
;VLog109=No
|
||||
;VLog110=No
|
||||
;VLog111=No
|
||||
;VLog112=No
|
||||
;VLog113=No
|
||||
;VLog114=No
|
||||
;VLog115=No
|
||||
;VLog116=No
|
||||
;VLog117=No
|
||||
;VLog118=No
|
||||
;VLog119=No
|
||||
;VLog120=No
|
104
apis/pypad/scripts/pypad_tunein.py
Executable file
104
apis/pypad/scripts/pypad_tunein.py
Executable file
@@ -0,0 +1,104 @@
|
||||
#!%PYTHON_BANGPATH%
|
||||
|
||||
# pypad_tunein.py
|
||||
#
|
||||
# Send PAD updates to TuneIn
|
||||
#
|
||||
# (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 os
|
||||
import sys
|
||||
import socket
|
||||
import requests
|
||||
import xml.etree.ElementTree as ET
|
||||
import syslog
|
||||
import pypad
|
||||
import configparser
|
||||
|
||||
def eprint(*args,**kwargs):
|
||||
print(pypad_name+': ',file=sys.stderr,end='')
|
||||
print(*args,file=sys.stderr)
|
||||
syslog.syslog(syslog.LOG_ERR,*args)
|
||||
|
||||
def iprint(*args,**kwargs):
|
||||
print(pypad_name+': ',file=sys.stdout,end='')
|
||||
print(*args,file=sys.stdout)
|
||||
syslog.syslog(syslog.LOG_INFO,*args)
|
||||
|
||||
def isTrue(string):
|
||||
l=['Yes','On','True','1']
|
||||
return string.lower() in map(str.lower,l)
|
||||
|
||||
def ProcessPad(update):
|
||||
if update.hasPadType(pypad.TYPE_NOW):
|
||||
n=1
|
||||
while(True):
|
||||
section='Station'+str(n)
|
||||
try:
|
||||
values={}
|
||||
values['id']=update.config().get(section,'StationID')
|
||||
values['partnerId']=update.config().get(section,'PartnerID')
|
||||
values['partnerKey']=update.config().get(section,'PartnerKey')
|
||||
values['title']=update.resolvePadFields(update.config().get(section,'TitleString'),pypad.ESCAPE_NONE)
|
||||
values['artist']=update.resolvePadFields(update.config().get(section,'ArtistString'),pypad.ESCAPE_NONE)
|
||||
values['album']=update.resolvePadFields(update.config().get(section,'AlbumString'),pypad.ESCAPE_NONE)
|
||||
iprint('Updating TuneIn: artist='+values['artist']+' title='+values['title']+' album='+values['album'])
|
||||
try:
|
||||
response=requests.get('http://air.radiotime.com/Playing.ashx',params=values)
|
||||
response.raise_for_status()
|
||||
except requests.exceptions.RequestException as e:
|
||||
eprint(str(e))
|
||||
else:
|
||||
xml=ET.fromstring(response.text)
|
||||
status=xml.find('./head/status')
|
||||
if(status.text!='200'):
|
||||
eprint('Update Failed: '+xml.find('./head/fault').text)
|
||||
n=n+1
|
||||
except configparser.NoSectionError:
|
||||
if(n==1):
|
||||
eprint('No station config found')
|
||||
return
|
||||
|
||||
#
|
||||
# Program Name
|
||||
#
|
||||
pypad_name=os.path.basename(__file__)
|
||||
|
||||
#
|
||||
# Open Syslog
|
||||
#
|
||||
syslog.openlog(pypad_name,logoption=syslog.LOG_PID,facility=syslog.LOG_DAEMON)
|
||||
|
||||
|
||||
#
|
||||
# Create Send Socket
|
||||
#
|
||||
send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
|
||||
|
||||
#
|
||||
# Start Receiver
|
||||
#
|
||||
rcvr=pypad.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_tunein.py: USAGE: cmd <hostname> <port> <config>')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
iprint('Started')
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
||||
iprint('Stopped')
|
105
apis/pypad/scripts/pypad_udp.exemplar
Normal file
105
apis/pypad/scripts/pypad_udp.exemplar
Normal file
@@ -0,0 +1,105 @@
|
||||
; This is the sample configuration for the 'pypad_udp.py' PyPAD script for
|
||||
; Rivendell, which can be used to output Now & Next data to one or more
|
||||
; remote UDP ports.
|
||||
|
||||
; Section Header
|
||||
;
|
||||
; One section per remote UDP port is configured, starting with 'Udp1' and
|
||||
; working up consecutively
|
||||
[Udp1]
|
||||
|
||||
; IP Address
|
||||
;
|
||||
; The IP address of the remote UDP port, in dotted-quad notation.
|
||||
IpAddress=127.0.0.1
|
||||
|
||||
; UDP Port
|
||||
;
|
||||
; The UDP port number of the remote UDP port, in the range 0 - 65,535.
|
||||
UdpPort=1234
|
||||
|
||||
; Format String. The string to be output each time RDAirPlay changes
|
||||
; play state, including any wildcards as placeholders for metadata values.
|
||||
;
|
||||
; The list of available wildcards can be found in the 'metadata_wildcards.txt'
|
||||
; file in the Rivendell documentation directory.
|
||||
;
|
||||
FormatString=NOW: %d(ddd MMM d hh:mm:ss yyyy): %t - %a\nNEXT: %D(ddd MMM d hh:mm:ss yyyy): %T - %A\n
|
||||
|
||||
; Encoding. Defines the set of escapes to be applied to the PAD fields.
|
||||
; The following options are available:
|
||||
;
|
||||
; 0 - Perform no character escaping.
|
||||
; 1 - "XML" escaping: Escape reserved characters as per XML-v1.0
|
||||
; 2 - "Web" escaping: Escape reserved characters as per RFC 2396 Section 2.4
|
||||
Encoding=0
|
||||
|
||||
; Null Update Handling. Defines how 'null' updates --i.e. those with a cart
|
||||
; number of '0' -- should be handled.
|
||||
;
|
||||
; 0 - Process all updates regardless of cart values.
|
||||
; 1 - Process update only if the 'now' cart is not null.
|
||||
; 2 - Process update only if the 'next' cart is not null.
|
||||
; 3 - Process update only if both the 'now' and 'next' carts are not null.
|
||||
ProcessNullUpdates=0
|
||||
|
||||
; Log Selection
|
||||
;
|
||||
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
|
||||
; state changes on that log should be output on this udp port. If set
|
||||
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
|
||||
; is active.
|
||||
MasterLog=Yes
|
||||
Aux1Log=Yes
|
||||
Aux2Log=Yes
|
||||
VLog101=No
|
||||
VLog102=No
|
||||
VLog103=No
|
||||
VLog104=No
|
||||
VLog105=No
|
||||
VLog106=No
|
||||
VLog107=No
|
||||
VLog108=No
|
||||
VLog109=No
|
||||
VLog110=No
|
||||
VLog111=No
|
||||
VLog112=No
|
||||
VLog113=No
|
||||
VLog114=No
|
||||
VLog115=No
|
||||
VLog116=No
|
||||
VLog117=No
|
||||
VLog118=No
|
||||
VLog119=No
|
||||
VLog120=No
|
||||
|
||||
|
||||
; Additional UDP destinations can be configured by adding new sections...
|
||||
;[Udp2]
|
||||
;FormatString=Artist: %a%r
|
||||
;IpAddress=192.168.10.22
|
||||
;UdpPort=6789
|
||||
;ProcessNullUpdates=0
|
||||
;MasterLog=Yes
|
||||
;Aux1Log=No
|
||||
;Aux2Log=Onair
|
||||
;VLog101=No
|
||||
;VLog102=No
|
||||
;VLog103=No
|
||||
;VLog104=No
|
||||
;VLog105=No
|
||||
;VLog106=No
|
||||
;VLog107=No
|
||||
;VLog108=No
|
||||
;VLog109=No
|
||||
;VLog110=No
|
||||
;VLog111=No
|
||||
;VLog112=No
|
||||
;VLog113=No
|
||||
;VLog114=No
|
||||
;VLog115=No
|
||||
;VLog116=No
|
||||
;VLog117=No
|
||||
;VLog118=No
|
||||
;VLog119=No
|
||||
;VLog120=No
|
58
apis/pypad/scripts/pypad_udp.py
Executable file
58
apis/pypad/scripts/pypad_udp.py
Executable file
@@ -0,0 +1,58 @@
|
||||
#!%PYTHON_BANGPATH%
|
||||
|
||||
# pypad_udp.py
|
||||
#
|
||||
# Send PAD updates via UDP
|
||||
#
|
||||
# (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 sys
|
||||
import socket
|
||||
import configparser
|
||||
import pypad
|
||||
|
||||
def eprint(*args,**kwargs):
|
||||
print(*args,file=sys.stderr,**kwargs)
|
||||
|
||||
def ProcessPad(update):
|
||||
n=1
|
||||
while(True):
|
||||
section='Udp'+str(n)
|
||||
try:
|
||||
if update.shouldBeProcessed(section):
|
||||
fmtstr=update.config().get(section,'FormatString')
|
||||
send_sock.sendto(update.resolvePadFields(fmtstr,int(update.config().get(section,'Encoding'))).encode('utf-8'),
|
||||
(update.config().get(section,'IpAddress'),int(update.config().get(section,'UdpPort'))))
|
||||
n=n+1
|
||||
except configparser.NoSectionError:
|
||||
return
|
||||
|
||||
#
|
||||
# 'Main' function
|
||||
#
|
||||
# Create Send Socket
|
||||
#
|
||||
send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
|
||||
|
||||
rcvr=pypad.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_udp.py: USAGE: cmd <hostname> <port> <config>')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
113
apis/pypad/scripts/pypad_urlwrite.exemplar
Normal file
113
apis/pypad/scripts/pypad_urlwrite.exemplar
Normal file
@@ -0,0 +1,113 @@
|
||||
; This is the configuration for the 'pypad_urlwrite' script for
|
||||
; Rivendell, which can be used to send one or more files to a local or
|
||||
; remote system using Now & Next data.
|
||||
|
||||
; Section Header
|
||||
;
|
||||
; One section per file to be written should be configured, starting with
|
||||
; 'Url1' and working up consecutively
|
||||
[Url1]
|
||||
|
||||
; Url
|
||||
;
|
||||
; The URL to which to send the file, which may contain filepath
|
||||
; wildcards as defined in Appendix C of the Rivendell Operations and
|
||||
; Administration Guide.
|
||||
;
|
||||
; The following URL schemes are recognized:
|
||||
; file://
|
||||
; ftp://
|
||||
; http:// (uses the PUT method)
|
||||
; sftp://
|
||||
;
|
||||
Url=sftp://server.example.com/myfile.txt
|
||||
|
||||
; Username
|
||||
;
|
||||
; The username to be used when authenticating to the remote server.
|
||||
;
|
||||
Username=someuser
|
||||
|
||||
; Password
|
||||
;
|
||||
; The password to be used when authenticating to the remote server.
|
||||
Password=hackme
|
||||
|
||||
; Format String. The string to be sent each time RDAirPlay changes
|
||||
; play state, including any wildcards as placeholders for metadata values.
|
||||
;
|
||||
; The list of available wildcards can be found in the 'Metadata Wildcards'
|
||||
; appendix of the Rivendell Operations Guide.
|
||||
;
|
||||
FormatString=NOW: %d(ddd MMM d hh:mm:ss yyyy): %t - %a\nNEXT: %D(ddd MMM d hh:mm:ss yyyy): %T - %A\n
|
||||
|
||||
; Encoding. Defines the set of escapes to be applied to the PAD fields.
|
||||
; The following options are available:
|
||||
;
|
||||
; 0 - Perform no character escaping.
|
||||
; 1 - "XML" escaping: Escape reserved characters as per XML-v1.0
|
||||
; 2 - "URL" escaping: Escape reserved characters as per RFC 2396 Section 2.4
|
||||
; 3 - "JSON" escaping: Escape reserved characters as per ECMA-404
|
||||
Encoding=0
|
||||
|
||||
; Log Selection
|
||||
;
|
||||
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
|
||||
; state changes on that log should be output. If set to 'Onair', then
|
||||
; output will be generated only if RDAirPlays OnAir flag is active.
|
||||
MasterLog=Yes
|
||||
Aux1Log=Yes
|
||||
Aux2Log=Yes
|
||||
VLog101=No
|
||||
VLog102=No
|
||||
VLog103=No
|
||||
VLog104=No
|
||||
VLog105=No
|
||||
VLog106=No
|
||||
VLog107=No
|
||||
VLog108=No
|
||||
VLog109=No
|
||||
VLog110=No
|
||||
VLog111=No
|
||||
VLog112=No
|
||||
VLog113=No
|
||||
VLog114=No
|
||||
VLog115=No
|
||||
VLog116=No
|
||||
VLog117=No
|
||||
VLog118=No
|
||||
VLog119=No
|
||||
VLog120=No
|
||||
|
||||
|
||||
; Additional files can be written by adding new sections...
|
||||
;
|
||||
;[Url2]
|
||||
;Url=file:///home/rd/somefile.txt
|
||||
;Username=janedoe
|
||||
;Password=crackme
|
||||
;FormatString=%t by %a\r\n
|
||||
;Encoding=0
|
||||
;MasterLog=Yes
|
||||
;Aux1Log=No
|
||||
;Aux2Log=Onair
|
||||
;VLog101=No
|
||||
;VLog102=No
|
||||
;VLog103=No
|
||||
;VLog104=No
|
||||
;VLog105=No
|
||||
;VLog106=No
|
||||
;VLog107=No
|
||||
;VLog108=No
|
||||
;VLog109=No
|
||||
;VLog110=No
|
||||
;VLog111=No
|
||||
;VLog112=No
|
||||
;VLog113=No
|
||||
;VLog114=No
|
||||
;VLog115=No
|
||||
;VLog116=No
|
||||
;VLog117=No
|
||||
;VLog118=No
|
||||
;VLog119=No
|
||||
;VLog120=No
|
69
apis/pypad/scripts/pypad_urlwrite.py
Executable file
69
apis/pypad/scripts/pypad_urlwrite.py
Executable file
@@ -0,0 +1,69 @@
|
||||
#!%PYTHON_BANGPATH%
|
||||
|
||||
# pypad_urlwrite.py
|
||||
#
|
||||
# Write PAD updates to arbitrary URLs
|
||||
#
|
||||
# (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 sys
|
||||
import syslog
|
||||
import configparser
|
||||
import pycurl
|
||||
import pypad
|
||||
from io import BytesIO
|
||||
|
||||
def eprint(*args,**kwargs):
|
||||
print(*args,file=sys.stderr,**kwargs)
|
||||
|
||||
def ProcessPad(update):
|
||||
n=1
|
||||
try:
|
||||
while(True):
|
||||
section='Url'+str(n)
|
||||
if update.shouldBeProcessed(section):
|
||||
fmtstr=update.config().get(section,'FormatString')
|
||||
buf=BytesIO(update.resolvePadFields(fmtstr,int(update.config().get(section,'Encoding'))).encode('utf-8'))
|
||||
curl=pycurl.Curl()
|
||||
curl.setopt(curl.URL,update.resolveFilepath(update.config().get(section,'Url'),update.dateTime()))
|
||||
curl.setopt(curl.USERNAME,update.config().get(section,'Username'))
|
||||
curl.setopt(curl.PASSWORD,update.config().get(section,'Password'))
|
||||
curl.setopt(curl.UPLOAD,True)
|
||||
curl.setopt(curl.READDATA,buf)
|
||||
try:
|
||||
curl.perform()
|
||||
except pycurl.error:
|
||||
syslog.syslog(syslog.LOG_WARNING,'['+section+'] failed: '+curl.errstr())
|
||||
curl.close()
|
||||
n=n+1
|
||||
|
||||
except configparser.NoSectionError:
|
||||
return
|
||||
|
||||
#
|
||||
# 'Main' function
|
||||
#
|
||||
syslog.openlog(sys.argv[0].split('/')[-1])
|
||||
|
||||
rcvr=pypad.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_urlwrite.py: USAGE: cmd <hostname> <port> <config>')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
89
apis/pypad/scripts/pypad_walltime.exemplar
Normal file
89
apis/pypad/scripts/pypad_walltime.exemplar
Normal file
@@ -0,0 +1,89 @@
|
||||
; This is the configuration for the 'pypad_walltime.py' script for
|
||||
; Rivendell, which can be used to output Now & Next data to one or more
|
||||
; Walltime clocks.
|
||||
|
||||
; Section Header
|
||||
;
|
||||
; One section per WallTime unit is configured, starting with 'Walltime1' and
|
||||
; working up consecutively
|
||||
[Walltime1]
|
||||
|
||||
; IP Address
|
||||
;
|
||||
; The IP address of the Walltime clock, in dotted-quad notation.
|
||||
IpAddress=192.168.21.100
|
||||
|
||||
; Password
|
||||
;
|
||||
; The password to use when sending HTML updates.
|
||||
Password=crackme
|
||||
|
||||
; Format String. The template for the HTML sent to the Walltime web display
|
||||
; each time RDAirPlay changes play state, including any wildcards as
|
||||
; placeholders for metadata values.
|
||||
;
|
||||
; The list of available wildcards can be found in the 'Metadata Wildcards'
|
||||
; appendix in the Rivendell Operations Guide.
|
||||
;
|
||||
FormatString=<body bgcolor="#000000"><table cellspacing="2" cellpadding="2" border="0"><tr><td> </td></tr><tr><td> </td></tr><tr><td><font size="6" color="white">%t</font></td></tr><tr><td> </td></tr><tr><td><font size="5" color="red">%a</font><td></tr></table></body>
|
||||
|
||||
; Log Selection
|
||||
;
|
||||
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
|
||||
; state changes on that log should be output on this udp port. If set
|
||||
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
|
||||
; is active.
|
||||
;
|
||||
MasterLog=Yes
|
||||
Aux1Log=No
|
||||
Aux2Log=No
|
||||
VLog101=No
|
||||
VLog102=No
|
||||
VLog103=No
|
||||
VLog104=No
|
||||
VLog105=No
|
||||
VLog106=No
|
||||
VLog107=No
|
||||
VLog108=No
|
||||
VLog109=No
|
||||
VLog110=No
|
||||
VLog111=No
|
||||
VLog112=No
|
||||
VLog113=No
|
||||
VLog114=No
|
||||
VLog115=No
|
||||
VLog116=No
|
||||
VLog117=No
|
||||
VLog118=No
|
||||
VLog119=No
|
||||
VLog120=No
|
||||
|
||||
|
||||
; Additional WallTime units can be configured by adding new sections...
|
||||
;[Walltime2]
|
||||
;FormatString=Artist: %a
|
||||
;IpAddress=192.168.10.22
|
||||
;UdpPort=6060
|
||||
;MasterLog=Yes
|
||||
;Aux1Log=No
|
||||
;Aux2Log=Onair
|
||||
;VLog101=No
|
||||
;VLog102=No
|
||||
;VLog103=No
|
||||
;VLog104=No
|
||||
;VLog105=No
|
||||
;VLog106=No
|
||||
;VLog107=No
|
||||
;VLog108=No
|
||||
;VLog109=No
|
||||
;VLog110=No
|
||||
;VLog111=No
|
||||
;VLog112=No
|
||||
;VLog113=No
|
||||
;VLog114=No
|
||||
;VLog115=No
|
||||
;VLog116=No
|
||||
;VLog117=No
|
||||
;VLog118=No
|
||||
;VLog119=No
|
||||
;VLog120=No
|
69
apis/pypad/scripts/pypad_walltime.py
Executable file
69
apis/pypad/scripts/pypad_walltime.py
Executable file
@@ -0,0 +1,69 @@
|
||||
#!%PYTHON_BANGPATH%
|
||||
|
||||
# pypad_walltime.py
|
||||
#
|
||||
# Write PAD updates a WallTime text widget.
|
||||
#
|
||||
# (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 sys
|
||||
import syslog
|
||||
import configparser
|
||||
import pycurl
|
||||
import pypad
|
||||
from io import BytesIO
|
||||
|
||||
def eprint(*args,**kwargs):
|
||||
print(*args,file=sys.stderr,**kwargs)
|
||||
|
||||
def ProcessPad(update):
|
||||
n=1
|
||||
try:
|
||||
while(True):
|
||||
section='Walltime'+str(n)
|
||||
if update.shouldBeProcessed(section):
|
||||
fmtstr=update.config().get(section,'FormatString')
|
||||
buf=BytesIO(update.resolvePadFields(fmtstr,pypad.ESCAPE_NONE).encode('utf-8'))
|
||||
curl=pycurl.Curl()
|
||||
curl.setopt(curl.URL,'http://'+update.config().get(section,'IpAddress')+'/webwidget');
|
||||
curl.setopt(curl.USERNAME,'user')
|
||||
curl.setopt(curl.PASSWORD,update.config().get(section,'Password'))
|
||||
curl.setopt(curl.UPLOAD,True)
|
||||
curl.setopt(curl.READDATA,buf)
|
||||
try:
|
||||
curl.perform()
|
||||
except pycurl.error:
|
||||
syslog.syslog(syslog.LOG_WARNING,'['+section+'] failed: '+curl.errstr())
|
||||
curl.close()
|
||||
n=n+1
|
||||
|
||||
except configparser.NoSectionError:
|
||||
return
|
||||
|
||||
#
|
||||
# 'Main' function
|
||||
#
|
||||
syslog.openlog(sys.argv[0].split('/')[-1])
|
||||
|
||||
rcvr=pypad.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_walltime.py: USAGE: cmd <hostname> <port> <config>')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
92
apis/pypad/scripts/pypad_xds.exemplar
Normal file
92
apis/pypad/scripts/pypad_xds.exemplar
Normal file
@@ -0,0 +1,92 @@
|
||||
; This is the configuration for the 'pypad_xds' script for
|
||||
; Rivendell, which can be used to output ISCI data for a Citidel X-Digital
|
||||
; Vantive system via UDP or serial.
|
||||
|
||||
; Section Header
|
||||
;
|
||||
; One section per Vantive UDP port is configured, starting with 'Udp1' and
|
||||
; working up consecutively
|
||||
[Udp1]
|
||||
|
||||
; IP Address
|
||||
;
|
||||
; The IP address of the remote UDP port, in dotted-quad notation.
|
||||
IpAddress=127.0.0.1
|
||||
|
||||
; UDP Port
|
||||
;
|
||||
; The UDP port number of the remote UDP port, in the range 0 - 65,535.
|
||||
UdpPort=1234
|
||||
|
||||
; TTY Device
|
||||
;
|
||||
; Use the specified TTY device instead of UDP to send CICs.
|
||||
;TtyDevice=/dev/ttyS0
|
||||
;TtySpeed=19200
|
||||
|
||||
; ISCI Prefix
|
||||
;
|
||||
; The string to prepend to transmitted ISCI code strings.
|
||||
;
|
||||
IsciPrefix=TEST_
|
||||
|
||||
; Log Selection
|
||||
;
|
||||
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
|
||||
; state changes on that log should be output on this port. If set
|
||||
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
|
||||
; is active.
|
||||
MasterLog=Yes
|
||||
Aux1Log=Yes
|
||||
Aux2Log=Yes
|
||||
VLog101=No
|
||||
VLog102=No
|
||||
VLog103=No
|
||||
VLog104=No
|
||||
VLog105=No
|
||||
VLog106=No
|
||||
VLog107=No
|
||||
VLog108=No
|
||||
VLog109=No
|
||||
VLog110=No
|
||||
VLog111=No
|
||||
VLog112=No
|
||||
VLog113=No
|
||||
VLog114=No
|
||||
VLog115=No
|
||||
VLog116=No
|
||||
VLog117=No
|
||||
VLog118=No
|
||||
VLog119=No
|
||||
VLog120=No
|
||||
|
||||
|
||||
; Additional Vantive destinations can be configured by adding new sections...
|
||||
;[Udp2]
|
||||
;IpAddress=192.168.10.22
|
||||
;UdpPort=6789
|
||||
;TtyDevice=/dev/ttyS1
|
||||
;TtySpeed=19200
|
||||
;MasterLog=Yes
|
||||
;Aux1Log=No
|
||||
;Aux2Log=Onair
|
||||
;VLog101=No
|
||||
;VLog102=No
|
||||
;VLog103=No
|
||||
;VLog104=No
|
||||
;VLog105=No
|
||||
;VLog106=No
|
||||
;VLog107=No
|
||||
;VLog108=No
|
||||
;VLog109=No
|
||||
;VLog110=No
|
||||
;VLog111=No
|
||||
;VLog112=No
|
||||
;VLog113=No
|
||||
;VLog114=No
|
||||
;VLog115=No
|
||||
;VLog116=No
|
||||
;VLog117=No
|
||||
;VLog118=No
|
||||
;VLog119=No
|
||||
;VLog120=No
|
104
apis/pypad/scripts/pypad_xds.py
Executable file
104
apis/pypad/scripts/pypad_xds.py
Executable file
@@ -0,0 +1,104 @@
|
||||
#!%PYTHON_BANGPATH%
|
||||
|
||||
# pypad_xds.py
|
||||
#
|
||||
# Send CICs via UDP or serial
|
||||
#
|
||||
# (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 sys
|
||||
import socket
|
||||
import configparser
|
||||
import serial
|
||||
import pypad
|
||||
|
||||
def eprint(*args,**kwargs):
|
||||
print(*args,file=sys.stderr,**kwargs)
|
||||
|
||||
|
||||
def FilterField(string):
|
||||
string=string.replace(' ','_')
|
||||
string=string.replace(',','_')
|
||||
string=string.replace('.','_')
|
||||
|
||||
string=string.replace('"',"'")
|
||||
|
||||
string=string.replace('%','-')
|
||||
string=string.replace('*','-')
|
||||
string=string.replace('+','-')
|
||||
string=string.replace('/','-')
|
||||
string=string.replace(':','-')
|
||||
string=string.replace(';','-')
|
||||
string=string.replace('<','-')
|
||||
string=string.replace('=','-')
|
||||
string=string.replace('>','-')
|
||||
string=string.replace('?','-')
|
||||
string=string.replace('@','-')
|
||||
string=string.replace('[','-')
|
||||
string=string.replace('\\','-')
|
||||
string=string.replace(']','-')
|
||||
string=string.replace('^','-')
|
||||
string=string.replace('{','-')
|
||||
string=string.replace('|','-')
|
||||
string=string.replace('}','-')
|
||||
|
||||
return string
|
||||
|
||||
|
||||
def ProcessPad(update):
|
||||
n=1
|
||||
while(True):
|
||||
section='Udp'+str(n)
|
||||
try:
|
||||
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW):
|
||||
packet='0:'+update.serviceProgramCode()+':'+update.config().get(section,'IsciPrefix')+FilterField(update.padField(pypad.TYPE_NOW,pypad.FIELD_EXTERNAL_EVENT_ID))+':*'
|
||||
try:
|
||||
#
|
||||
# Use serial output
|
||||
#
|
||||
tty_dev=update.config().get(section,'TtyDevice')
|
||||
speed=int(update.config().get(section,'TtySpeed'))
|
||||
parity=serial.PARITY_NONE
|
||||
dev=serial.Serial(tty_dev,speed,parity=parity,bytesize=8)
|
||||
dev.write(packet.encode('utf-8'))
|
||||
dev.close()
|
||||
|
||||
except configparser.NoOptionError:
|
||||
#
|
||||
# Use UDP output
|
||||
#
|
||||
send_sock.sendto(packet.encode('utf-8'),
|
||||
(update.config().get(section,'IpAddress'),int(update.config().get(section,'UdpPort'))))
|
||||
n=n+1
|
||||
except configparser.NoSectionError:
|
||||
return
|
||||
|
||||
#
|
||||
# 'Main' function
|
||||
#
|
||||
# Create Send Socket
|
||||
#
|
||||
send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
|
||||
|
||||
rcvr=pypad.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_xds.py: USAGE: cmd <hostname> <port> <config>')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
112
apis/pypad/scripts/pypad_xmpad.exemplar
Executable file
112
apis/pypad/scripts/pypad_xmpad.exemplar
Executable file
@@ -0,0 +1,112 @@
|
||||
; This is the configuration for the 'pypad_xmpad.py' script for
|
||||
; Rivendell, which can be used to output Now & Next data for an XM
|
||||
; Satellite Radio channel.
|
||||
;
|
||||
; NOTE: The serial ports configured here have NOTHING TO DO with the
|
||||
; ports configured in rdadmin(1)! These ports are used strictly by this
|
||||
; script, and will not be usable by any other Rivendell component.
|
||||
|
||||
; Section Header
|
||||
;
|
||||
; One per serial device to be configured, starting with 'Serial1' and
|
||||
; working up consecutively
|
||||
[Serial1]
|
||||
|
||||
; Serial Device
|
||||
;
|
||||
; The device file that corresponds to the serial device.
|
||||
;Device=/dev/ttyS0
|
||||
Device=/dev/ttyUSB0
|
||||
|
||||
; Serial Baud Rate (in bps)
|
||||
Speed=4800
|
||||
|
||||
; Parity (0=none, 1=even, 2=odd)
|
||||
Parity=0
|
||||
|
||||
; Number of bits per data 'word'.
|
||||
WordSize=8
|
||||
|
||||
; Program ID
|
||||
; A unique integer value, assigned by XM
|
||||
ProgramID=1000000005
|
||||
|
||||
; Format Strings. There is one for each line of PAD data (total=2).
|
||||
; The string is output each time RDAirPlay changes
|
||||
; play state, including any wildcards as placeholders for metadata values.
|
||||
;
|
||||
; The list of available wildcards can be found in the 'Metadata Wildcards'
|
||||
; appendix in the Rivendell Operations Guide.
|
||||
;
|
||||
FormatString1=%t
|
||||
FormatString2=%a
|
||||
|
||||
; Display Size. The maximum length of text to be sent for each line.
|
||||
DisplaySize1=8
|
||||
DisplaySize2=10
|
||||
|
||||
; Record Flag. Set to 'Yes' to allow recording, or 'No' to disable.
|
||||
Recording=Yes
|
||||
|
||||
; Log Selection
|
||||
;
|
||||
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
|
||||
; state changes on that log should be output on this serial port. If set
|
||||
; to 'Onair', then output will be generated only if RDAirPlay's OnAir flag
|
||||
; is active.
|
||||
MasterLog=Yes
|
||||
Aux1Log=Yes
|
||||
Aux2Log=Yes
|
||||
VLog101=No
|
||||
VLog102=No
|
||||
VLog103=No
|
||||
VLog104=No
|
||||
VLog105=No
|
||||
VLog106=No
|
||||
VLog107=No
|
||||
VLog108=No
|
||||
VLog109=No
|
||||
VLog110=No
|
||||
VLog111=No
|
||||
VLog112=No
|
||||
VLog113=No
|
||||
VLog114=No
|
||||
VLog115=No
|
||||
VLog116=No
|
||||
VLog117=No
|
||||
VLog118=No
|
||||
VLog119=No
|
||||
VLog120=No
|
||||
|
||||
|
||||
; Additional serial ports can be configured by adding new sections...
|
||||
;[Serial2]
|
||||
;Device=/dev/ttyS1
|
||||
;Speed=4800
|
||||
;Parity=0
|
||||
;WordSize=8
|
||||
;FormatString1=%t
|
||||
;FormatString2=%a
|
||||
;MasterLog=Yes
|
||||
;Aux1Log=No
|
||||
;Aux2Log=Onair
|
||||
;VLog101=No
|
||||
;VLog102=No
|
||||
;VLog103=No
|
||||
;VLog104=No
|
||||
;VLog105=No
|
||||
;VLog106=No
|
||||
;VLog107=No
|
||||
;VLog108=No
|
||||
;VLog109=No
|
||||
;VLog110=No
|
||||
;VLog111=No
|
||||
;VLog112=No
|
||||
;VLog113=No
|
||||
;VLog114=No
|
||||
;VLog115=No
|
||||
;VLog116=No
|
||||
;VLog117=No
|
||||
;VLog118=No
|
||||
;VLog119=No
|
||||
;VLog120=No
|
184
apis/pypad/scripts/pypad_xmpad.py
Executable file
184
apis/pypad/scripts/pypad_xmpad.py
Executable file
@@ -0,0 +1,184 @@
|
||||
#!%PYTHON_BANGPATH%
|
||||
|
||||
# pypad_xmpad.py
|
||||
#
|
||||
# Write PAD updates to a Sirius/XM channel
|
||||
#
|
||||
# (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 sys
|
||||
import syslog
|
||||
import configparser
|
||||
import serial
|
||||
import pypad
|
||||
|
||||
XMPAD_DELIMITER='\02'
|
||||
# XMPAD_DELIMITER='\x7c' #(|)
|
||||
|
||||
def eprint(*args,**kwargs):
|
||||
print(*args,file=sys.stderr,**kwargs)
|
||||
|
||||
|
||||
def MakeA4(update,section):
|
||||
a4='A4'
|
||||
|
||||
a4+=XMPAD_DELIMITER+'1'
|
||||
|
||||
field1=update.resolvePadFields(update.config().get(section,'FormatString1'),
|
||||
pypad.ESCAPE_NONE)
|
||||
a4+=XMPAD_DELIMITER+field1[0:16]
|
||||
|
||||
a4+='\n'
|
||||
|
||||
return a4
|
||||
|
||||
def MakeA5(update,section):
|
||||
a5='A5'
|
||||
|
||||
a5+=XMPAD_DELIMITER+'1'
|
||||
|
||||
field2=update.resolvePadFields(update.config().get(section,'FormatString2'),
|
||||
pypad.ESCAPE_NONE)
|
||||
a5+=XMPAD_DELIMITER+field2[0:16]
|
||||
|
||||
a5+='\n'
|
||||
|
||||
return a5
|
||||
|
||||
|
||||
def MakeB4(update,section):
|
||||
b4='B-4'
|
||||
|
||||
#
|
||||
# Recording flag
|
||||
#
|
||||
recording='1'
|
||||
if update.config().get(section,'Recording').lower()=='yes':
|
||||
recording='0'
|
||||
b4+=XMPAD_DELIMITER+recording
|
||||
|
||||
b4+=XMPAD_DELIMITER+'1'
|
||||
|
||||
#
|
||||
# Event duration
|
||||
#
|
||||
b4+=XMPAD_DELIMITER+str(update.padField(pypad.TYPE_NOW,pypad.FIELD_LENGTH)//432)
|
||||
|
||||
b4+=XMPAD_DELIMITER+'0'
|
||||
|
||||
#
|
||||
# Display field sizes
|
||||
#
|
||||
disp1size=update.config().get(section,'DisplaySize1')
|
||||
mask1=''
|
||||
for i in range(int(disp1size)):
|
||||
mask1+='1'
|
||||
disp2size=update.config().get(section,'DisplaySize2')
|
||||
mask2=''
|
||||
for i in range(int(disp2size)):
|
||||
mask2+='1'
|
||||
disp2size=update.config().get(section,'DisplaySize2')
|
||||
b4+=XMPAD_DELIMITER+disp1size
|
||||
b4+=XMPAD_DELIMITER+disp2size
|
||||
b4+=XMPAD_DELIMITER+mask1
|
||||
b4+=XMPAD_DELIMITER+mask2
|
||||
|
||||
#
|
||||
# Display field values
|
||||
#
|
||||
field1=update.resolvePadFields(update.config().get(section,'FormatString1'),
|
||||
pypad.ESCAPE_NONE)
|
||||
field2=update.resolvePadFields(update.config().get(section,'FormatString2'),
|
||||
pypad.ESCAPE_NONE)
|
||||
b4+=XMPAD_DELIMITER+field2[0:16]
|
||||
b4+=XMPAD_DELIMITER+mask1
|
||||
b4+=XMPAD_DELIMITER+mask2
|
||||
b4+=XMPAD_DELIMITER+field1[0:16]
|
||||
|
||||
#
|
||||
# Program ID
|
||||
#
|
||||
b4+=XMPAD_DELIMITER+update.config().get(section,'ProgramID')
|
||||
|
||||
#
|
||||
# EOM
|
||||
#
|
||||
b4+='\n'
|
||||
|
||||
return b4
|
||||
|
||||
|
||||
def OpenSerialDevice(config,section):
|
||||
devname=config.get(section,'Device')
|
||||
speed=int(config.get(section,'Speed'))
|
||||
parity=serial.PARITY_NONE
|
||||
if int(config.get(section,'Parity'))==1:
|
||||
parity=serial.PARITY_EVEN
|
||||
if int(config.get(section,'Parity'))==2:
|
||||
parity=serial.PARITY_ODD
|
||||
bytesize=int(config.get(section,'WordSize'))
|
||||
return serial.Serial(devname,speed,parity=parity,bytesize=bytesize)
|
||||
|
||||
|
||||
def ProcessTimer(config):
|
||||
n=1
|
||||
try:
|
||||
while(True):
|
||||
section='Serial'+str(n)
|
||||
dev=OpenSerialDevice(config,section)
|
||||
dev.write('H0\n'.encode('utf-8'))
|
||||
dev.close()
|
||||
n=n+1
|
||||
|
||||
except configparser.NoSectionError:
|
||||
return
|
||||
|
||||
def ProcessPad(update):
|
||||
n=1
|
||||
try:
|
||||
while(True):
|
||||
section='Serial'+str(n)
|
||||
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW):
|
||||
dev=OpenSerialDevice(update.config(),section)
|
||||
b4=MakeB4(update,section)
|
||||
a4=MakeA4(update,section)
|
||||
a5=MakeA5(update,section)
|
||||
dev.write(b4.encode('utf-8'))
|
||||
dev.write(b4.encode('utf-8'))
|
||||
dev.write(b4.encode('utf-8'))
|
||||
dev.write(a4.encode('utf-8'))
|
||||
dev.write(a5.encode('utf-8'))
|
||||
dev.close()
|
||||
n=n+1
|
||||
|
||||
except configparser.NoSectionError:
|
||||
return
|
||||
|
||||
#
|
||||
# 'Main' function
|
||||
#
|
||||
syslog.openlog(sys.argv[0].split('/')[-1])
|
||||
|
||||
rcvr=pypad.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_xmpad.py: USAGE: cmd <hostname> <port> <config>')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
rcvr.setTimerCallback(30,ProcessTimer)
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
Reference in New Issue
Block a user