mirror of
https://github.com/ElvishArtisan/rivendell.git
synced 2025-10-11 00:53:53 +02:00
Merge remote-tracking branch 'upstream/rlm2' into rlm2
Conflicts: ChangeLog apis/PyPAD/scripts/Makefile.am
This commit is contained in:
@@ -21,7 +21,7 @@
|
||||
## Use automake to process this into a Makefile.in
|
||||
|
||||
SUBDIRS = api\
|
||||
examples\
|
||||
scripts\
|
||||
tests
|
||||
|
||||
CLEANFILES = *~\
|
||||
|
@@ -20,7 +20,10 @@
|
||||
|
||||
import configparser
|
||||
import datetime
|
||||
import MySQLdb
|
||||
import signal
|
||||
import socket
|
||||
import sys
|
||||
import json
|
||||
|
||||
#
|
||||
@@ -715,6 +718,16 @@ class Receiver(object):
|
||||
def __PyPAD_Process(self,pad):
|
||||
self.__callback(pad)
|
||||
|
||||
def __getDbCredentials(self):
|
||||
config=configparser.ConfigParser()
|
||||
config.readfp(open('/etc/rd.conf'))
|
||||
return (config.get('mySQL','Loginname'),config.get('mySQL','Password'),
|
||||
config.get('mySQL','Hostname'),config.get('mySQL','Database'))
|
||||
|
||||
def __openDb(self):
|
||||
creds=self.__getDbCredentials()
|
||||
return MySQLdb.connect(creds[2],creds[0],creds[1],creds[3])
|
||||
|
||||
def setCallback(self,cb):
|
||||
"""
|
||||
Set the processing callback.
|
||||
@@ -727,11 +740,28 @@ class Receiver(object):
|
||||
the 'PyPAD.Update::config()' method will return a parserconfig
|
||||
object created from the specified file. The file must be in INI
|
||||
format.
|
||||
|
||||
A special case is if the supplied filename string begins with
|
||||
the '$' character. If so, the remainder of the string is assumed
|
||||
to be an unsigned integer ID that is used to retrieve the
|
||||
configuration from the 'PYPAD_INSTANCES' table in the database
|
||||
pointed to by '/etc/rd.conf'.
|
||||
"""
|
||||
fp=open(filename)
|
||||
self.__config_parser=configparser.ConfigParser(interpolation=None)
|
||||
self.__config_parser.readfp(fp)
|
||||
fp.close()
|
||||
if filename[0]=='$': # Get the config from the DB
|
||||
db=self.__openDb()
|
||||
cursor=db.cursor()
|
||||
cursor.execute('select CONFIG from PYPAD_INSTANCES where ID='+
|
||||
filename[1::])
|
||||
config=cursor.fetchone()
|
||||
self.__config_parser=configparser.ConfigParser(interpolation=None)
|
||||
self.__config_parser.read_string(config[0])
|
||||
db.close()
|
||||
|
||||
else: # Get the config from a file
|
||||
fp=open(filename)
|
||||
self.__config_parser=configparser.ConfigParser(interpolation=None)
|
||||
self.__config_parser.readfp(fp)
|
||||
fp.close()
|
||||
|
||||
def start(self,hostname,port):
|
||||
"""
|
||||
@@ -746,6 +776,9 @@ class Receiver(object):
|
||||
port - The TCP port to connect to. For most cases, just use
|
||||
'PyPAD.PAD_TCP_PORT'.
|
||||
"""
|
||||
# So we exit cleanly when shutdown by rdpadengined(8)
|
||||
signal.signal(signal.SIGTERM,SigHandler)
|
||||
|
||||
sock=socket.socket(socket.AF_INET)
|
||||
conn=sock.connect((hostname,port))
|
||||
c=bytes()
|
||||
@@ -763,3 +796,5 @@ class Receiver(object):
|
||||
line=bytes()
|
||||
|
||||
|
||||
def SigHandler(signo,stack):
|
||||
sys.exit(0)
|
||||
|
@@ -22,23 +22,32 @@
|
||||
|
||||
install-exec-am:
|
||||
mkdir -p $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/PyPAD
|
||||
../../../helpers/install_python.sh now_and_next.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/PyPAD/now_and_next.py
|
||||
../../../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_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
|
||||
|
||||
uninstall-local:
|
||||
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/PyPAD/now_and_next.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_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
|
||||
|
||||
EXTRA_DIST = now_and_next.py\
|
||||
EXTRA_DIST = pypad_filewrite.exemplar\
|
||||
pypad_filewrite.py\
|
||||
pypad_icecast2.exemplar\
|
||||
pypad_icecast2.py\
|
||||
pypad_tunein.exemplar\
|
||||
pypad_tunein.py\
|
||||
pypad_udp.exemplar\
|
||||
pypad_udp.py
|
||||
|
||||
CLEANFILES = *~\
|
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
|
@@ -50,9 +50,9 @@ def ProcessPad(update):
|
||||
#
|
||||
rcvr=PyPAD.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[1])
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_filewrite.py: you must specify a configuration file')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
rcvr.start('localhost',PyPAD.PAD_TCP_PORT)
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
108
apis/PyPAD/scripts/pypad_icecast2.exemplar
Normal file
108
apis/PyPAD/scripts/pypad_icecast2.exemplar
Normal file
@@ -0,0 +1,108 @@
|
||||
; pypad_icecast2.conf
|
||||
;
|
||||
; This is the sample configuration file for the 'pypad_icecast2.py' module 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=%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
|
100
apis/PyPAD/scripts/pypad_icecast2.py
Executable file
100
apis/PyPAD/scripts/pypad_icecast2.py
Executable file
@@ -0,0 +1,100 @@
|
||||
#!%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.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
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_URL)
|
||||
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(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('You must specify a configuration file')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
iprint('Started')
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
||||
iprint('Stopped')
|
108
apis/PyPAD/scripts/pypad_tunein.exemplar
Normal file
108
apis/PyPAD/scripts/pypad_tunein.exemplar
Normal file
@@ -0,0 +1,108 @@
|
||||
; pypad_tunein.conf
|
||||
;
|
||||
; This is the sample configuration file for the 'pypad_tunein' module 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
|
106
apis/PyPAD/scripts/pypad_tunein.py
Executable file
106
apis/PyPAD/scripts/pypad_tunein.py
Executable file
@@ -0,0 +1,106 @@
|
||||
#!%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.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
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_URL)
|
||||
values['artist']=update.resolvePadFields(update.config().get(section,'ArtistString'),PyPAD.ESCAPE_URL)
|
||||
values['album']=update.resolvePadFields(update.config().get(section,'AlbumString'),PyPAD.ESCAPE_URL)
|
||||
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(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('You must specify a configuration file')
|
||||
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
|
@@ -50,9 +50,9 @@ send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
|
||||
|
||||
rcvr=PyPAD.Receiver()
|
||||
try:
|
||||
rcvr.setConfigFile(sys.argv[1])
|
||||
rcvr.setConfigFile(sys.argv[3])
|
||||
except IndexError:
|
||||
eprint('pypad_udp.py: you must specify a configuration file')
|
||||
sys.exit(1)
|
||||
rcvr.setCallback(ProcessPad)
|
||||
rcvr.start("localhost",PyPAD.PAD_TCP_PORT)
|
||||
rcvr.start(sys.argv[1],int(sys.argv[2]))
|
@@ -20,7 +20,8 @@
|
||||
##
|
||||
## Use automake to process this into a Makefile.in
|
||||
|
||||
EXTRA_DIST = filepath_test.py
|
||||
EXTRA_DIST = filepath_test.py\
|
||||
now_and_next.py\
|
||||
pad_test.py
|
||||
|
||||
CLEANFILES = *~\
|
||||
|
Reference in New Issue
Block a user