From dcfdc8ccbfde328e3e219fd03dc5a502ded06110 Mon Sep 17 00:00:00 2001 From: Patrick Linstruth Date: Mon, 10 Dec 2018 07:00:13 -0800 Subject: [PATCH 01/15] Fixed a bug that was causing rdairplay(1) to segfault when no log was loaded. --- ChangeLog | 3 +++ lib/rdlogplay.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 1e71c8cd..119daba1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -18094,3 +18094,6 @@ 2018-11-30 Patrick Linstruth * Fixed regression with rdimport(1) that threw SQL errors when importing into an existing cart. +2018-12-10 Patrick Linstruth + * Fixed a bug that was causing rdairplay(1) to segfault when no + log was loaded. diff --git a/lib/rdlogplay.cpp b/lib/rdlogplay.cpp index 53f27d9f..5242e4f9 100644 --- a/lib/rdlogplay.cpp +++ b/lib/rdlogplay.cpp @@ -2719,7 +2719,7 @@ void RDLogPlay::Playing(int id) emit played(line); AdvanceActiveEvent(); UpdatePostPoint(); - if (isRefreshable()&&play_log->autoRefresh()) { + if (play_log!=NULL&&isRefreshable()&&play_log->autoRefresh()) { refresh(); } LogPlayEvent(logline); From 7392ba4198a32ea07341d51e9625ecde3d9e36ed Mon Sep 17 00:00:00 2001 From: Patrick Linstruth Date: Mon, 10 Dec 2018 19:14:59 -0800 Subject: [PATCH 02/15] Added a 'pypad_tunein.py' PyPAD script. --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index 94615112..af4e0c44 100644 --- a/ChangeLog +++ b/ChangeLog @@ -18141,3 +18141,5 @@ 'PyPAD.Update::resolvePadFields()'. * Added a 'PyPAD.Update::padField()' method. * Added a 'PyPAD.Update::escape()' method. +2018-12-10 Patrick Linstruth + * Added a 'pypad_tunein.py' PyPAD script. From be7b02b1264da395346611ada3558afa2942f23d Mon Sep 17 00:00:00 2001 From: Patrick Linstruth Date: Mon, 10 Dec 2018 19:15:06 -0800 Subject: [PATCH 03/15] Initial commit --- apis/PyPAD/examples/pypad_tunein.py | 136 ++++++++++++++++++++++++++++ conf/pypad_tunein.conf | 114 +++++++++++++++++++++++ 2 files changed, 250 insertions(+) create mode 100755 apis/PyPAD/examples/pypad_tunein.py create mode 100644 conf/pypad_tunein.conf diff --git a/apis/PyPAD/examples/pypad_tunein.py b/apis/PyPAD/examples/pypad_tunein.py new file mode 100755 index 00000000..c0a951c4 --- /dev/null +++ b/apis/PyPAD/examples/pypad_tunein.py @@ -0,0 +1,136 @@ +#!/usr/bin/python + +# pypad_tunein.py +# +# Send PAD updates to TuneIn +# +# (C) Copyright 2018 Fred Gleason +# +# 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 urllib +import xml.etree.ElementTree as ET +import syslog +import PyPAD +try: + import configparser +except ImportError: + import ConfigParser as configparser + +def eprint(*args,**kwargs): + print(pypad_name+': ',file=sys.stderr,end='',**kwargs) + print(*args,file=sys.stderr,**kwargs) + syslog.syslog(syslog.LOG_ERR,*args) + +def iprint(*args,**kwargs): + print(pypad_name+': ',file=sys.stdout,end='',**kwargs) + print(*args,sep=pypad_name+': ',file=sys.stdout,**kwargs) + 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']=config.get(section,'StationID') + values['partnerId']=config.get(section,'PartnerID') + values['partnerKey']=config.get(section,'PartnerKey') + values['title']=update.resolvePadFields(config.get(section,'TitleString'),PyPAD.ESCAPE_URL) + values['artist']=update.resolvePadFields(config.get(section,'ArtistString'),PyPAD.ESCAPE_URL) + values['album']=update.resolvePadFields(config.get(section,'AlbumString'),PyPAD.ESCAPE_URL) + url_values=urllib.urlencode(values) + url='http://air.radiotime.com/Playing.ashx?'+url_values + iprint('Updating TuneIn: artist='+values['artist']+' title='+values['title']+' album='+values['album']) + try: + response=urllib.urlopen(url) + except: + eprint(url+' code='+str(response.getcode())) + else: + xml=ET.fromstring(response.read()) + 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) + +# +# Read Configuration +# +if len(sys.argv)>=2: + fp=open(sys.argv[1]) + config=configparser.ConfigParser() + config.readfp(fp) + fp.close() +else: + eprint('You must specify a configuration file') + sys.exit(1) + +# +# Daemonize +# +try: + daemon=config.get(section,'Daemonize') +except: + daemon='0' + +if isTrue(daemon): + try: + pid=os.fork() + except: + eprint('Could not fork') + sys.exit(1) + + if (pid==0): + os.setsid() + else: + exit(0) + +# +# Create Send Socket +# +send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) + +# +# Start Receiver +# +rcvr=PyPAD.Receiver() +rcvr.setCallback(ProcessPad) +iprint('Started') +rcvr.start("localhost",PyPAD.PAD_TCP_PORT) +iprint('Stopped') diff --git a/conf/pypad_tunein.conf b/conf/pypad_tunein.conf new file mode 100644 index 00000000..8f207964 --- /dev/null +++ b/conf/pypad_tunein.conf @@ -0,0 +1,114 @@ +; 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] + + +; Daemonize +; +; Run pypad_tunein as a daemon 'Yes' or 'No'. +Daemonize=Yes + + +; 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 From 4d5081156e83dc541ba3aca8a0f1ec25ed615eba Mon Sep 17 00:00:00 2001 From: Patrick Linstruth Date: Tue, 11 Dec 2018 16:46:48 -0800 Subject: [PATCH 04/15] Undo commit --- lib/rdlogplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rdlogplay.cpp b/lib/rdlogplay.cpp index 21475ee8..16229541 100644 --- a/lib/rdlogplay.cpp +++ b/lib/rdlogplay.cpp @@ -2728,7 +2728,7 @@ void RDLogPlay::Playing(int id) emit played(line); AdvanceActiveEvent(); UpdatePostPoint(); - if (play_log!=NULL&&isRefreshable()&&play_log->autoRefresh()) { + if (isRefreshable()&&play_log->autoRefresh()) { refresh(); } LogPlayEvent(logline); From 306d5354be29bb671f8f20928d22d2852bdfff46 Mon Sep 17 00:00:00 2001 From: Patrick Linstruth Date: Tue, 11 Dec 2018 17:12:49 -0800 Subject: [PATCH 05/15] Remove Daemonize option --- apis/PyPAD/examples/pypad_tunein.py | 19 ------------------- conf/pypad_tunein.conf | 6 ------ 2 files changed, 25 deletions(-) diff --git a/apis/PyPAD/examples/pypad_tunein.py b/apis/PyPAD/examples/pypad_tunein.py index c0a951c4..fc384a40 100755 --- a/apis/PyPAD/examples/pypad_tunein.py +++ b/apis/PyPAD/examples/pypad_tunein.py @@ -101,25 +101,6 @@ else: eprint('You must specify a configuration file') sys.exit(1) -# -# Daemonize -# -try: - daemon=config.get(section,'Daemonize') -except: - daemon='0' - -if isTrue(daemon): - try: - pid=os.fork() - except: - eprint('Could not fork') - sys.exit(1) - - if (pid==0): - os.setsid() - else: - exit(0) # # Create Send Socket diff --git a/conf/pypad_tunein.conf b/conf/pypad_tunein.conf index 8f207964..f607c033 100644 --- a/conf/pypad_tunein.conf +++ b/conf/pypad_tunein.conf @@ -13,12 +13,6 @@ [Station1] -; Daemonize -; -; Run pypad_tunein as a daemon 'Yes' or 'No'. -Daemonize=Yes - - ; StationID ; ; The Station ID of the TuneIn station to which to send updates. From 15976fbe6fb117f45689b42d3fb1ebf1d19c7682 Mon Sep 17 00:00:00 2001 From: Patrick Linstruth Date: Wed, 12 Dec 2018 15:20:05 -0800 Subject: [PATCH 06/15] Updated to use Python 3 --- apis/PyPAD/examples/pypad_tunein.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/PyPAD/examples/pypad_tunein.py b/apis/PyPAD/examples/pypad_tunein.py index fc384a40..7f871b11 100755 --- a/apis/PyPAD/examples/pypad_tunein.py +++ b/apis/PyPAD/examples/pypad_tunein.py @@ -25,7 +25,7 @@ from __future__ import print_function import os import sys import socket -import urllib +import requests import xml.etree.ElementTree as ET import syslog import PyPAD From f695dce5121e567634cf2f801fee25de76cd16ec Mon Sep 17 00:00:00 2001 From: Patrick Linstruth Date: Wed, 12 Dec 2018 15:24:05 -0800 Subject: [PATCH 07/15] Fix commit and add %PYTHON_BANGPATH% --- apis/PyPAD/examples/pypad_tunein.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/apis/PyPAD/examples/pypad_tunein.py b/apis/PyPAD/examples/pypad_tunein.py index 7f871b11..4dcedb77 100755 --- a/apis/PyPAD/examples/pypad_tunein.py +++ b/apis/PyPAD/examples/pypad_tunein.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#%PYTHON_BANGPATH% # pypad_tunein.py # @@ -29,10 +29,7 @@ import requests import xml.etree.ElementTree as ET import syslog import PyPAD -try: - import configparser -except ImportError: - import ConfigParser as configparser +import configparser def eprint(*args,**kwargs): print(pypad_name+': ',file=sys.stderr,end='',**kwargs) @@ -61,15 +58,14 @@ def ProcessPad(update): values['title']=update.resolvePadFields(config.get(section,'TitleString'),PyPAD.ESCAPE_URL) values['artist']=update.resolvePadFields(config.get(section,'ArtistString'),PyPAD.ESCAPE_URL) values['album']=update.resolvePadFields(config.get(section,'AlbumString'),PyPAD.ESCAPE_URL) - url_values=urllib.urlencode(values) - url='http://air.radiotime.com/Playing.ashx?'+url_values iprint('Updating TuneIn: artist='+values['artist']+' title='+values['title']+' album='+values['album']) try: - response=urllib.urlopen(url) - except: - eprint(url+' code='+str(response.getcode())) + 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.read()) + xml=ET.fromstring(response.text) status=xml.find('./head/status') if(status.text!='200'): eprint('Update Failed: '+xml.find('./head/fault').text) @@ -94,7 +90,7 @@ syslog.openlog(logoption=syslog.LOG_PID, facility=syslog.LOG_DAEMON) # if len(sys.argv)>=2: fp=open(sys.argv[1]) - config=configparser.ConfigParser() + config=configparser.ConfigParser(interpolation=None) config.readfp(fp) fp.close() else: From 6912ca1ebe9be274ec2abc300d1bc9a617a89931 Mon Sep 17 00:00:00 2001 From: Patrick Linstruth Date: Wed, 12 Dec 2018 15:32:27 -0800 Subject: [PATCH 08/15] Fixed error in eprint and iprint --- apis/PyPAD/examples/pypad_tunein.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apis/PyPAD/examples/pypad_tunein.py b/apis/PyPAD/examples/pypad_tunein.py index 4dcedb77..059eed4f 100755 --- a/apis/PyPAD/examples/pypad_tunein.py +++ b/apis/PyPAD/examples/pypad_tunein.py @@ -1,4 +1,4 @@ -#%PYTHON_BANGPATH% +#!/usr/bin/python3.6 # pypad_tunein.py # @@ -32,13 +32,13 @@ import PyPAD import configparser def eprint(*args,**kwargs): - print(pypad_name+': ',file=sys.stderr,end='',**kwargs) - print(*args,file=sys.stderr,**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='',**kwargs) - print(*args,sep=pypad_name+': ',file=sys.stdout,**kwargs) + print(pypad_name+': ',file=sys.stdout,end='') + print(*args,file=sys.stdout) syslog.syslog(syslog.LOG_INFO,*args) def isTrue(string): From d2dd8fb13b46cddcb4f252ea71cfcb39797ec149 Mon Sep 17 00:00:00 2001 From: Patrick Linstruth Date: Thu, 13 Dec 2018 22:16:11 -0800 Subject: [PATCH 09/15] Keeping up with the PyPAD API evolution --- apis/PyPAD/examples/pypad_tunein.py | 31 +++++++++++------------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/apis/PyPAD/examples/pypad_tunein.py b/apis/PyPAD/examples/pypad_tunein.py index 059eed4f..75039032 100755 --- a/apis/PyPAD/examples/pypad_tunein.py +++ b/apis/PyPAD/examples/pypad_tunein.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3.6 +#!%PYTHON_BANGPATH% # pypad_tunein.py # @@ -52,12 +52,12 @@ def ProcessPad(update): section='Station'+str(n) try: values={} - values['id']=config.get(section,'StationID') - values['partnerId']=config.get(section,'PartnerID') - values['partnerKey']=config.get(section,'PartnerKey') - values['title']=update.resolvePadFields(config.get(section,'TitleString'),PyPAD.ESCAPE_URL) - values['artist']=update.resolvePadFields(config.get(section,'ArtistString'),PyPAD.ESCAPE_URL) - values['album']=update.resolvePadFields(config.get(section,'AlbumString'),PyPAD.ESCAPE_URL) + 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) @@ -85,18 +85,6 @@ pypad_name=os.path.basename(__file__) # syslog.openlog(logoption=syslog.LOG_PID, facility=syslog.LOG_DAEMON) -# -# Read Configuration -# -if len(sys.argv)>=2: - fp=open(sys.argv[1]) - config=configparser.ConfigParser(interpolation=None) - config.readfp(fp) - fp.close() -else: - eprint('You must specify a configuration file') - sys.exit(1) - # # Create Send Socket @@ -107,6 +95,11 @@ send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Start Receiver # rcvr=PyPAD.Receiver() +try: + rcvr.setConfigFile(sys.argv[1]) +except IndexError: + eprint('You must specify a configuration file') + sys.exit(1) rcvr.setCallback(ProcessPad) iprint('Started') rcvr.start("localhost",PyPAD.PAD_TCP_PORT) From 8316ee3b32a5215b564360020e34894919b5f848 Mon Sep 17 00:00:00 2001 From: Patrick Linstruth Date: Thu, 13 Dec 2018 22:18:39 -0800 Subject: [PATCH 10/15] Added pypad_tunein.py to install --- apis/PyPAD/examples/Makefile.am | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apis/PyPAD/examples/Makefile.am b/apis/PyPAD/examples/Makefile.am index b983c146..771d473c 100644 --- a/apis/PyPAD/examples/Makefile.am +++ b/apis/PyPAD/examples/Makefile.am @@ -24,15 +24,18 @@ 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 + ../../../helpers/install_python.sh pypad_tunein.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/PyPAD/pypad_tunein.py ../../../helpers/install_python.sh pypad_udp.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/PyPAD/pypad_udp.py 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.py + rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/PyPAD/pypad_tunein.py rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/PyPAD/pypad_udp.py EXTRA_DIST = now_and_next.py\ pypad_filewrite.py\ + pypad_tunein.py\ pypad_udp.py CLEANFILES = *~\ From 887bfd55903294906dc1f124e4008ab59018fa81 Mon Sep 17 00:00:00 2001 From: Patrick Linstruth Date: Fri, 14 Dec 2018 12:58:54 -0800 Subject: [PATCH 11/15] Add pypad_icecast2.py --- ChangeLog | 3 +- apis/PyPAD/examples/pypad_icecast2.py | 100 ++++++++++++++++++++++++ conf/pypad_icecast2.conf | 108 ++++++++++++++++++++++++++ 3 files changed, 210 insertions(+), 1 deletion(-) create mode 100755 apis/PyPAD/examples/pypad_icecast2.py create mode 100644 conf/pypad_icecast2.conf diff --git a/ChangeLog b/ChangeLog index 5d084856..4fb70b28 100644 --- a/ChangeLog +++ b/ChangeLog @@ -18190,5 +18190,6 @@ 'PyPAD.Update::shouldBeProcessed()' method. * Updated the 'pypad_udp.py' script to use the 'PyPAD.Update::shouldBeProcessed()' method. -2018-12-13 Patrick Linstruth +2018-12-14 Patrick Linstruth * Added a 'pypad_tunein.py' PyPAD script. + * Added a 'pypad_icecast2.py' PyPAD script. diff --git a/apis/PyPAD/examples/pypad_icecast2.py b/apis/PyPAD/examples/pypad_icecast2.py new file mode 100755 index 00000000..34baaaa8 --- /dev/null +++ b/apis/PyPAD/examples/pypad_icecast2.py @@ -0,0 +1,100 @@ +#!%PYTHON_BANGPATH% + +# pypad_icecast2.py +# +# Send PAD updates to Icecast2 mountpoint +# +# (C) Copyright 2018 Fred Gleason +# +# 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[1]) +except IndexError: + eprint('You must specify a configuration file') + sys.exit(1) +rcvr.setCallback(ProcessPad) +iprint('Started') +rcvr.start("localhost",PyPAD.PAD_TCP_PORT) +iprint('Stopped') diff --git a/conf/pypad_icecast2.conf b/conf/pypad_icecast2.conf new file mode 100644 index 00000000..55dcddf5 --- /dev/null +++ b/conf/pypad_icecast2.conf @@ -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 From f3e7906a9f7f0e6f603d26e62eb1aadc666f5d77 Mon Sep 17 00:00:00 2001 From: Patrick Linstruth Date: Fri, 14 Dec 2018 13:00:51 -0800 Subject: [PATCH 12/15] Add pypad_icecast2 to install --- apis/PyPAD/examples/Makefile.am | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apis/PyPAD/examples/Makefile.am b/apis/PyPAD/examples/Makefile.am index 771d473c..cd6aaccb 100644 --- a/apis/PyPAD/examples/Makefile.am +++ b/apis/PyPAD/examples/Makefile.am @@ -24,17 +24,20 @@ 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 + ../../../helpers/install_python.sh pypad_icecast2.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/PyPAD/pypad_icecast2.py ../../../helpers/install_python.sh pypad_tunein.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/PyPAD/pypad_tunein.py ../../../helpers/install_python.sh pypad_udp.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/PyPAD/pypad_udp.py 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.py + rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/PyPAD/pypad_icecast2.py rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/PyPAD/pypad_tunein.py rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/PyPAD/pypad_udp.py EXTRA_DIST = now_and_next.py\ pypad_filewrite.py\ + pypad_icecast2.py\ pypad_tunein.py\ pypad_udp.py From 7259c14b3958587ef78e5e7670ca17c6423f8717 Mon Sep 17 00:00:00 2001 From: Patrick Linstruth Date: Mon, 17 Dec 2018 12:35:33 -0800 Subject: [PATCH 13/15] Removed unnecessary files --- apis/PyPAD/examples/pypad_icecast2.py | 100 ------------------------ apis/PyPAD/examples/pypad_tunein.py | 106 ------------------------- conf/pypad_icecast2.conf | 108 -------------------------- conf/pypad_tunein.conf | 108 -------------------------- 4 files changed, 422 deletions(-) delete mode 100755 apis/PyPAD/examples/pypad_icecast2.py delete mode 100755 apis/PyPAD/examples/pypad_tunein.py delete mode 100644 conf/pypad_icecast2.conf delete mode 100644 conf/pypad_tunein.conf diff --git a/apis/PyPAD/examples/pypad_icecast2.py b/apis/PyPAD/examples/pypad_icecast2.py deleted file mode 100755 index 34baaaa8..00000000 --- a/apis/PyPAD/examples/pypad_icecast2.py +++ /dev/null @@ -1,100 +0,0 @@ -#!%PYTHON_BANGPATH% - -# pypad_icecast2.py -# -# Send PAD updates to Icecast2 mountpoint -# -# (C) Copyright 2018 Fred Gleason -# -# 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[1]) -except IndexError: - eprint('You must specify a configuration file') - sys.exit(1) -rcvr.setCallback(ProcessPad) -iprint('Started') -rcvr.start("localhost",PyPAD.PAD_TCP_PORT) -iprint('Stopped') diff --git a/apis/PyPAD/examples/pypad_tunein.py b/apis/PyPAD/examples/pypad_tunein.py deleted file mode 100755 index 75039032..00000000 --- a/apis/PyPAD/examples/pypad_tunein.py +++ /dev/null @@ -1,106 +0,0 @@ -#!%PYTHON_BANGPATH% - -# pypad_tunein.py -# -# Send PAD updates to TuneIn -# -# (C) Copyright 2018 Fred Gleason -# -# 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[1]) -except IndexError: - eprint('You must specify a configuration file') - sys.exit(1) -rcvr.setCallback(ProcessPad) -iprint('Started') -rcvr.start("localhost",PyPAD.PAD_TCP_PORT) -iprint('Stopped') diff --git a/conf/pypad_icecast2.conf b/conf/pypad_icecast2.conf deleted file mode 100644 index 55dcddf5..00000000 --- a/conf/pypad_icecast2.conf +++ /dev/null @@ -1,108 +0,0 @@ -; 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 diff --git a/conf/pypad_tunein.conf b/conf/pypad_tunein.conf deleted file mode 100644 index f607c033..00000000 --- a/conf/pypad_tunein.conf +++ /dev/null @@ -1,108 +0,0 @@ -; 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 From 306b0c3d87c252f0c57cccf9a70f89117fba3fa4 Mon Sep 17 00:00:00 2001 From: Patrick Linstruth Date: Wed, 19 Dec 2018 09:15:06 -0800 Subject: [PATCH 14/15] Update to new PyPAD API --- apis/PyPAD/scripts/pypad_icecast2.exemplar | 8 +++----- apis/PyPAD/scripts/pypad_icecast2.py | 2 -- apis/PyPAD/scripts/pypad_tunein.exemplar | 9 +++------ apis/PyPAD/scripts/pypad_tunein.py | 2 -- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/apis/PyPAD/scripts/pypad_icecast2.exemplar b/apis/PyPAD/scripts/pypad_icecast2.exemplar index 55dcddf5..908918e6 100644 --- a/apis/PyPAD/scripts/pypad_icecast2.exemplar +++ b/apis/PyPAD/scripts/pypad_icecast2.exemplar @@ -1,9 +1,7 @@ -; 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 +; This is the sample configuration file 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 ; diff --git a/apis/PyPAD/scripts/pypad_icecast2.py b/apis/PyPAD/scripts/pypad_icecast2.py index 6277cd6e..4c25deee 100755 --- a/apis/PyPAD/scripts/pypad_icecast2.py +++ b/apis/PyPAD/scripts/pypad_icecast2.py @@ -20,8 +20,6 @@ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # -from __future__ import print_function - import os import sys import socket diff --git a/apis/PyPAD/scripts/pypad_tunein.exemplar b/apis/PyPAD/scripts/pypad_tunein.exemplar index f607c033..c0fdb775 100644 --- a/apis/PyPAD/scripts/pypad_tunein.exemplar +++ b/apis/PyPAD/scripts/pypad_tunein.exemplar @@ -1,10 +1,7 @@ -; pypad_tunein.conf +; This is the sample configuration file for the 'pypad_tunein.py' PyPAD script for +; Rivendell, which can be used to update the metadata at TuneIn server +; using Now & Next data. ; -; 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 ; diff --git a/apis/PyPAD/scripts/pypad_tunein.py b/apis/PyPAD/scripts/pypad_tunein.py index 56b2af39..f1606967 100755 --- a/apis/PyPAD/scripts/pypad_tunein.py +++ b/apis/PyPAD/scripts/pypad_tunein.py @@ -20,8 +20,6 @@ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # -from __future__ import print_function - import os import sys import socket From aa7080911be1073de0ae1133854b87b3d29ba66d Mon Sep 17 00:00:00 2001 From: Patrick Linstruth Date: Wed, 19 Dec 2018 15:39:27 -0800 Subject: [PATCH 15/15] Fix syslog process name --- apis/PyPAD/scripts/pypad_icecast2.py | 2 +- apis/PyPAD/scripts/pypad_tunein.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apis/PyPAD/scripts/pypad_icecast2.py b/apis/PyPAD/scripts/pypad_icecast2.py index 4c25deee..2a83f528 100755 --- a/apis/PyPAD/scripts/pypad_icecast2.py +++ b/apis/PyPAD/scripts/pypad_icecast2.py @@ -75,7 +75,7 @@ pypad_name=os.path.basename(__file__) # # Open Syslog # -syslog.openlog(logoption=syslog.LOG_PID, facility=syslog.LOG_DAEMON) +syslog.openlog(pypad_name,logoption=syslog.LOG_PID,facility=syslog.LOG_DAEMON) # diff --git a/apis/PyPAD/scripts/pypad_tunein.py b/apis/PyPAD/scripts/pypad_tunein.py index f1606967..6897f82c 100755 --- a/apis/PyPAD/scripts/pypad_tunein.py +++ b/apis/PyPAD/scripts/pypad_tunein.py @@ -81,7 +81,7 @@ pypad_name=os.path.basename(__file__) # # Open Syslog # -syslog.openlog(logoption=syslog.LOG_PID, facility=syslog.LOG_DAEMON) +syslog.openlog(pypad_name,logoption=syslog.LOG_PID,facility=syslog.LOG_DAEMON) #