Merge branch 'master' of dev.sourcefabric.org:campcaster

This commit is contained in:
Naomi 2011-02-22 11:46:22 -05:00
commit 42598d03b4
15 changed files with 148 additions and 2680 deletions

28
INSTALL
View File

@ -1,29 +1,23 @@
--------------------------------------------------------------------------------
Copyright (c) 2010 Sourcefabric O.P.S.
Copyright (c) 2010-2011 Sourcefabric O.P.S.
This file is part of the Airtime project.
http://airtime.sourcefabric.org/
To report bugs, send an e-mail to contact@sourcefabric.org
To report bugs, visit our bug tracker at:
http://dev.sourcefabric.org/browse/CC
Airtime 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.
Airtime 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 Airtime; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Visit our community support forum here:
http://forum.sourcefabric.org/index.php/f/14/
For commercial support, see http://sourcefabric.org/en/services/about/347/Support.htm
or send an e-mail to contact@sourcefabric.org
--------------------------------------------------------------------------------
Please see this page for a typical user install:
http://www.sourcefabric.org/en/products/airtime_manuals/
Please see this page for install instructions:
http://wiki.sourcefabric.org/display/CC/Installing+Airtime+%28v1.6%29
If you are a developer, see this page:
http://wiki.sourcefabric.org/display/CC

View File

@ -1,37 +1,72 @@
This application uses the following 3rd Party software:
* Zend Framework
------------
Linked code:
------------
* Zend Framework 1.10.3
- Web site: http://framework.zend.com/
- License: New BSD license
- Does this link with our code? Yes
- Compatible with GPLv3: Yes
* Liquidsoap
- License: GPLv2
- Does this link with our code? No, we only call this as an executable
* PEAR
- Notes: We only use the PEAR base class PEAR_Error, in the "PEAR" PEAR library.
- License: New BSD License
- Does this link with our code? Yes
- Compatible with GPLv3? Yes.
* GetID3
* GetID3
- Web site: http://getid3.sourceforge.net/
- License: GPLv2
- Does this link with our code? Yes
- Compatible with GPLv3? Yes
* mp3cut from the package poc-streamer
- Does this link with our code? No, it is an externally called executable file.
* PHP
- Does this link with our code? No, it is the interpreter.
* Propel ORM
- Web site: http://www.propelorm.org/
- License: MIT/Expat License
- Does this link with our code? Yes
- Compatible with the GPL: Yes. See http://www.gnu.org/licenses/license-list.html
* Phing
* Phing
- Web site: http://phing.info/trac/
- Note: Only used for development, not needed to run Airtime.
- License: LGPLv3
----------------
Non-linked code:
----------------
* Linux
* Apache Web Server 2.2
- Web site: http://httpd.apache.org/
* PostgreSQL 8.4
- Web site: http://www.postgresql.org/
- License: The PostgreSQL License. See http://www.opensource.org/licenses/postgresql
* PHP 5.3
- Web site: http://www.php.net/
- License: The PHP License. See http://www.php.net/license/3_01.txt
* Python 2.6
- Web site: http://www.python.org/
- License: PSF License. See http://docs.python.org/license.html
* Liquidsoap (pre-release of 1.0)
- Web site: http://savonet.sourceforge.net/
- License: GPLv2
* mp3cut from the package poc-streamer
* jQuery
- Web site: http://jquery.com/
- License: MIT and GPL. See http://jquery.org/license
- jQuery components used:
* Full Calendar
- Web site: http://arshaw.com/fullcalendar/
- License: Dual licensed under MIT and GPLv2
* Colorpicker
- Web site: http://www.eyecon.ro/colorpicker/
- License: Dual licensed under the MIT and GPL licenses.

View File

@ -14,7 +14,7 @@ import sys
import time
import urllib
import logging
from util import json
import json
import os
from urlparse import urlparse
@ -118,7 +118,7 @@ class AirTimeApiClient(ApiClientInterface):
response = urllib.urlopen(url)
data = response.read()
logger.debug("Data: %s", data)
response_json = json.read(data)
response_json = json.loads(data)
version = response_json['version']
logger.debug("Airtime Version %s detected", version)
except Exception, e:
@ -223,7 +223,7 @@ class AirTimeApiClient(ApiClientInterface):
try:
response_json = urllib.urlopen(export_url).read()
#logger.debug("%s", response_json)
response = json.read(response_json)
response = json.loads(response_json)
#logger.info("export status %s", response['check'])
status = response['check']
except Exception, e:
@ -281,7 +281,7 @@ class AirTimeApiClient(ApiClientInterface):
try:
response = urllib.urlopen(url)
response = json.read(response.read())
response = json.loads(response.read())
logger.info("API-Status %s", response['status'])
logger.info("API-Message %s", response['message'])
@ -302,7 +302,7 @@ class AirTimeApiClient(ApiClientInterface):
if (data[0] != '{'):
return response
try:
data = json.read(data)
data = json.loads(data)
logger.debug(str(data))
schedule_id = data["schedule_id"]
url = self.config["base_url"] + self.config["api_base"] + self.config["update_start_playing_url"]
@ -311,7 +311,7 @@ class AirTimeApiClient(ApiClientInterface):
url = url.replace("%%api_key%%", self.config["api_key"])
logger.debug(url)
response = urllib.urlopen(url)
response = json.read(response.read())
response = json.loads(response.read())
logger.info("API-Status %s", response['status'])
logger.info("API-Message %s", response['message'])
@ -329,7 +329,7 @@ class AirTimeApiClient(ApiClientInterface):
#
#try:
# response = urllib.urlopen(url, self.api_auth)
# response = json.read(response.read())
# response = json.loads(response.read())
# logger.debug("Trying to contact %s", url)
# logger.info("API-Status %s", response['status'])
# logger.info("API-Message %s", response['message'])
@ -349,7 +349,7 @@ class AirTimeApiClient(ApiClientInterface):
data["schedule_id"] = playlist['id']
except Exception, e:
data["schedule_id"] = 0
data = json.write(data)
data = json.dumps(data)
return data
@ -404,7 +404,7 @@ class ObpApiClient():
try:
logger.debug("Trying to contact %s", url)
response = urllib.urlopen(url, self.api_auth)
response_json = json.read(response.read())
response_json = json.loads(response.read())
obp_version = int(response_json['version'])
logger.debug("OBP Version %s detected", obp_version)
@ -471,7 +471,7 @@ class ObpApiClient():
try:
response_json = urllib.urlopen(export_url).read()
logger.debug("%s", response_json)
response = json.read(response_json)
response = json.loads(response_json)
logger.info("export status %s", response['check'])
status = response['check']
except Exception, e:
@ -502,7 +502,7 @@ class ObpApiClient():
try:
response = urllib.urlopen(url, self.api_auth)
response = json.read(response.read())
response = json.loads(response.read())
logger.info("API-Status %s", response['status'])
logger.info("API-Message %s", response['message'])
@ -536,7 +536,7 @@ class ObpApiClient():
try:
response = urllib.urlopen(url, self.api_auth)
response = json.read(response.read())
response = json.loads(response.read())
logger.info("API-Status %s", response['status'])
logger.info("API-Message %s", response['message'])
logger.info("TXT %s", response['str_dls'])
@ -556,7 +556,7 @@ class ObpApiClient():
try:
response = urllib.urlopen(url, self.api_auth)
response = json.read(response.read())
response = json.loads(response.read())
logger.debug("Trying to contact %s", url)
logger.info("API-Status %s", response['status'])
logger.info("API-Message %s", response['message'])
@ -580,6 +580,6 @@ class ObpApiClient():
data["playlist_id"] = 0
data["user_id"] = 0
data["transmission_id"] = 0
data = json.write(data)
data = json.dumps(data)
return data

View File

@ -8,8 +8,6 @@ import time
import logging
from util import json
import os
import socket
@ -87,4 +85,4 @@ class DlsClient():

View File

@ -62,8 +62,8 @@ def get_current_script_dir():
try:
current_script_dir = get_current_script_dir()
print "Terminating any existing pypo processes"
os.system("python %s/pypo-stop.py"% current_script_dir)
print "Checking and removing any existing pypo processes"
os.system("python %s/pypo-uninstall.py 2>&1 1>/dev/null"% current_script_dir)
time.sleep(5)
# Create users

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -19,12 +19,6 @@ Attention & ToDos
- liquidsoap does not like mono files! So we have to make sure that only files with
2 channels are fed to LiquidSoap
(solved: current = audio_to_stereo(current) - maybe not with ultimate performance)
made for python version 2.5!!
should work with 2.6 as well with a bit of adaption. for
sure the json parsing has to be changed
(2.6 has an parser, pypo brings it's own -> util/json.py)
"""
# python defaults (debian default)
@ -296,17 +290,7 @@ class Playout:
elif int(playlist['subtype']) > 0 and int(playlist['subtype']) < 5:
ls_playlist = self.handle_media_file(playlist, pkey, ls_playlist)
"""
This is kind of hackish. We add a bunch of "silence" tracks to the end of each playlist.
So we can make sure the list does not get repeated just before a new one is called.
(or in case nothing is in the scheduler afterwards)
20 x silence = 10 hours
"""
for i in range (0, 1):
ls_playlist += self.silence_file + "\n"
print '',
# write playlist file
plfile = open(self.cache_dir + str(pkey) + '/list.lsp', "w")
plfile.write(ls_playlist)
@ -679,29 +663,12 @@ class Playout:
logger.debug('OK - Can read playlist file')
pl_file = open(src, "r")
"""
i know this could be wrapped, maybe later..
"""
tn = telnetlib.Telnet(LS_HOST, 1234)
for line in pl_file.readlines():
line = line.strip()
logger.debug(line)
tn.write(self.export_source + '.push %s' % (line))
tn.write("\n")
tn.write("exit\n")
logger.debug(tn.read_all())
pattern = '%Y-%m-%d-%H-%M-%S'
#strptime returns struct_time in local time
#mktime takes a time_struct and returns a floating point
#gmtime Convert a time expressed in seconds since the epoch to a struct_time in UTC
#gmtime Convert a time expressed in seconds since the epoch to a struct_time in UTC
#mktime: expresses the time in local time, not UTC. It returns a floating point number, for compatibility with time().
epoch_start = calendar.timegm(time.gmtime(time.mktime(time.strptime(pkey, pattern))))
epoch_start = calendar.timegm(time.gmtime(time.mktime(time.strptime(pkey, '%Y-%m-%d-%H-%M-%S'))))
#Return the time as a floating point number expressed in seconds since the epoch, in UTC.
epoch_now = time.time()
@ -717,8 +684,19 @@ class Playout:
logger.debug('sleeping for %s s' % (sleep_time))
time.sleep(sleep_time)
tn = telnetlib.Telnet(LS_HOST, 1234)
for line in pl_file.readlines():
line = line.strip()
logger.debug(line)
tn.write('queue.push %s' % (line))
tn.write("\n")
tn.write("exit\n")
logger.debug(tn.read_all())
logger.debug('sending "flip"')
"""
tn = telnetlib.Telnet(LS_HOST, 1234)
# Get any extra information for liquidsoap (which will be sent back to us)
@ -733,6 +711,7 @@ class Playout:
tn.write("exit\n")
tn.read_all()
"""
status = 1
except Exception, e:
logger.error('%s', e)

View File

@ -29,6 +29,7 @@ import logging.config
import urllib
import urllib2
import string
import json
# additional modules (should be checked)
from configobj import ConfigObj
@ -50,13 +51,7 @@ parser = OptionParser(usage=usage)
# Options
parser.add_option("-d", "--data", help="Pass JSON data from liquidsoap into this script.", metavar="data")
#parser.add_option("-p", "--playing", help="Tell server what is playing right now.", default=False, action="store_true", dest="playing")
#parser.add_option("-t", "--playlist-type", help="", metavar="playlist_type")
parser.add_option("-m", "--media-id", help="ID of the file that is currently playing.", metavar="media_id")
#parser.add_option("-U", "--user-id", help="", metavar="user_id")
#parser.add_option("-P", "--playlist-id", help="", metavar="playlist_id")
#parser.add_option("-T", "--transmission-id", help="", metavar="transmission_id")
#parser.add_option("-E", "--export-source", help="", metavar="export_source")
# parse options
(options, args) = parser.parse_args()
@ -72,135 +67,21 @@ except Exception, e:
print 'error: ', e
sys.exit()
class Global:
def __init__(self):
print
def selfcheck(self):
pass
#self.api_client = api_client.api_client_factory(config)
#self.api_client.check_version()
class Notify:
def __init__(self):
self.api_client = api_client.api_client_factory(config)
#self.dls_client = DlsClient('127.0.0.128', 50008, 'myusername', 'mypass')
def notify_media_start_playing(self, data, media_id):
logger = logging.getLogger()
#tnow = time.localtime(time.time())
logger.debug('#################################################')
logger.debug('# Calling server to update about what\'s playing #')
logger.debug('#################################################')
logger.debug('data = '+ str(data))
#print 'options.data = '+ options.data
#data = json.read(options.data)
response = self.api_client.notify_media_item_start_playing(data, media_id)
logger.debug("Response: "+str(response))
#def start_playing(self, options):
# logger = logging.getLogger("start_playing")
# tnow = time.localtime(time.time())
#
# #print options
#
# logger.debug('#################################################')
# logger.debug('# Calling server to update about what\'s playing #')
# logger.debug('#################################################')
#
# if int(options.playlist_type) < 5:
# logger.debug('seems to be a playlist')
#
# try:
# media_id = int(options.media_id)
# except Exception, e:
# media_id = 0
#
# response = self.api_client.update_start_playing(options.playlist_type, options.export_source, media_id, options.playlist_id, options.transmission_id)
#
# logger.debug(response)
#
# if int(options.playlist_type) == 6:
# logger.debug('seems to be a couchcast')
#
# try:
# media_id = int(options.media_id)
# except Exception, e:
# media_id = 0
#
# response = self.api_client.update_start_playing(options.playlist_type, options.export_source, media_id, options.playlist_id, options.transmission_id)
#
# logger.debug(response)
#
# sys.exit()
#def start_playing_legacy(self, options):
# logger = logging.getLogger("start_playing")
# tnow = time.localtime(time.time())
#
# print '#################################################'
# print '# Calling server to update about what\'s playing #'
# print '#################################################'
#
# path = options
#
# print
# print path
# print
#
# if 'pl_id' in path:
# print 'seems to be a playlist'
# type = 'playlist'
# id = path[5:]
#
# elif 'text' in path:
# print 'seems to be a playlist'
# type = 'text'
# id = path[4:]
# print id
#
# else:
# print 'seems to be a single track (media)'
# type = 'media'
# try:
# file = path.split("/")[-1:][0]
# if file.find('_cue_') > 0:
# id = file.split("_cue_")[0]
# else:
# id = file.split(".")[-2:][0]
#
# except Exception, e:
# #print e
# id = False
#
# try:
# id = id
# except Exception, e:
# #print e
# id = False
#
# print
# print type + " id: ",
# print id
#
#
# response = self.api_client.update_start_playing(type, id, self.export_source, path)
#
# print 'DONE'
#
# try:
# txt = response['txt']
# print '#######################################'
# print txt
# print '#######################################'
# #self.dls_client.set_txt(txt)
#
# except Exception, e:
# print e
if __name__ == '__main__':
print
@ -210,13 +91,8 @@ if __name__ == '__main__':
print '#########################################'
# initialize
g = Global()
logger = logging.getLogger()
#if options.playing:
# try: n.start_playing(options)
# except Exception, e:
# print e
# sys.exit()
if not options.data:
print "NOTICE: 'data' command-line argument not given."
sys.exit()
@ -226,7 +102,6 @@ if __name__ == '__main__':
sys.exit()
try:
g.selfcheck()
n = Notify()
n.notify_media_start_playing(options.data, options.media_id)
except Exception, e:

View File

@ -28,7 +28,8 @@ icecast_port = 8000
icecast_pass = "hackme"
# mountpoints
mount_scheduler = "airtime.mp3"
mount_point_mp3 = "airtime.mp3"
mount_point_vorbis = "airtime.ogg"
# mount intra is used for scheduler >>> fallback stream
mount_intra = "pypo_intra"
@ -38,3 +39,10 @@ intra_host = "127.0.0.1"
intra_port = 9000
intra_pass = "hackme"
icecast_url = "http://airtime.sourcefabric.org"
icecast_description = "Airtime Radio!"
icecast_genre = "genre"
output_sound_device = true
output_icecast_vorbis = true
output_icecast_mp3 = true

View File

@ -5,42 +5,14 @@ set("log.file.path", log_file)
set("log.stdout", true)
set("server.telnet", true)
active_queue = ref 0
scheduler_q0 = request.queue(conservative=true,length=600.,id="scheduler_q0")
scheduler_q1 = request.queue(conservative=true,length=600.,id="scheduler_q1")
scheduler_q0 = audio_to_stereo(scheduler_q0)
scheduler_q1 = audio_to_stereo(scheduler_q1)
queue = request.queue(conservative=true,length=600.,id="queue")
queue = audio_to_stereo(queue)
pypo_data = ref '0'
# push function, enqueues file in inactive queue (does not start automatically)
def scheduler_push(s)
ignore(server.execute("scheduler_q#{!active_queue}.push #{s}"))
print('push to #{!active_queue} - #{s}')
"Done"
end
# flips the queues
def scheduler_flip()
# get playing (active) queue and flush it
l = list.hd(server.execute("scheduler_q#{!active_queue}.queue"))
l = string.split(separator=" ",l)
list.iter(fun (rid) -> ignore(server.execute("scheduler_q#{!active_queue}.ignore #{rid}")), l)
# skip the playing item
source.skip(if !active_queue==0 then scheduler_q0 else scheduler_q1 end)
# flip variables
active_queue := 1-!active_queue
print('switch to active queue: #{!active_queue}')
"Done"
end
def notify(m)
print("./notify.sh --data='#{!pypo_data}' --media-id=#{m['media_id']}")
end
#def notify(m)
# print("./notify.sh --data='#{!pypo_data}' --media-id=#{m['media_id']}")
#end
def crossfade(s)
s = fade.in(type="log", s)
@ -49,23 +21,42 @@ def crossfade(s)
cross(fader,s)
end
server.register(namespace="scheduler","push", scheduler_push)
server.register(namespace="scheduler","flip", fun (s) -> begin scheduler_flip() end)
server.register(namespace="vars", "pypo_data", fun (s) -> begin pypo_data := s "Done" end)
default = single("/opt/pypo/files/basic/silence.mp3")
radio = fallback(track_sensitive=false, [switch(track_sensitive=false, [(fun () -> !active_queue==1, scheduler_q0), (fun () -> !active_queue==0, scheduler_q1)]), default])
default = rewrite_metadata([("artist","Airtime"), ("title", "offline")],default)
#radio = on_metadata(notify, radio)
s = fallback(track_sensitive=false, [queue, default])
radio = crossfade(radio)
#s = on_metadata(notify, s)
s = crossfade(s)
out(radio)
clock(id="clock_icecast",
output.icecast(%mp3,
host = icecast_host, port = icecast_port,
password = icecast_pass, mount = mount_scheduler,
fallible = true,
restart = true,
restart_delay = 5,
buffer(radio)))
if output_sound_device then
out_device = out(s)
end
if output_icecast_mp3 then
out_mp3 = output.icecast(%mp3,
host = icecast_host, port = icecast_port,
password = icecast_pass, mount = mount_point_mp3,
fallible = true,
restart = true,
restart_delay = 5,
url = icecast_url,
description = icecast_description,
genre = icecast_genre,
s)
end
if output_icecast_vorbis then
out_vorbis = output.icecast(%vorbis,
host = icecast_host, port = icecast_port,
password = icecast_pass, mount = mount_point_vorbis,
fallible = true,
restart = true,
restart_delay = 5,
url = icecast_url,
description = icecast_description,
genre = icecast_genre,
s)
end

View File

@ -1,48 +0,0 @@
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3
/opt/pypo/files/basic/silence.mp3

View File

@ -1,310 +0,0 @@
import string
import types
## json.py implements a JSON (http://json.org) reader and writer.
## Copyright (C) 2005 Patrick D. Logan
## Contact mailto:patrickdlogan@stardecisions.com
##
## This library is free software; you can redistribute it and/or
## modify it under the terms of the GNU Lesser General Public
## License as published by the Free Software Foundation; either
## version 2.1 of the License, or (at your option) any later version.
##
## This library 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
## Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public
## License along with this library; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
class _StringGenerator(object):
def __init__(self, string):
self.string = string
self.index = -1
def peek(self):
i = self.index + 1
if i < len(self.string):
return self.string[i]
else:
return None
def next(self):
self.index += 1
if self.index < len(self.string):
return self.string[self.index]
else:
raise StopIteration
def all(self):
return self.string
class WriteException(Exception):
pass
class ReadException(Exception):
pass
class JsonReader(object):
hex_digits = {'A': 10,'B': 11,'C': 12,'D': 13,'E': 14,'F':15}
escapes = {'t':'\t','n':'\n','f':'\f','r':'\r','b':'\b'}
def read(self, s):
self._generator = _StringGenerator(s)
result = self._read()
return result
def _read(self):
self._eatWhitespace()
peek = self._peek()
if peek is None:
raise ReadException, "Nothing to read: '%s'" % self._generator.all()
if peek == '{':
return self._readObject()
elif peek == '[':
return self._readArray()
elif peek == '"':
return self._readString()
elif peek == '-' or peek.isdigit():
return self._readNumber()
elif peek == 't':
return self._readTrue()
elif peek == 'f':
return self._readFalse()
elif peek == 'n':
return self._readNull()
elif peek == '/':
self._readComment()
return self._read()
else:
raise ReadException, "Input is not valid JSON: '%s'" % self._generator.all()
def _readTrue(self):
self._assertNext('t', "true")
self._assertNext('r', "true")
self._assertNext('u', "true")
self._assertNext('e', "true")
return True
def _readFalse(self):
self._assertNext('f', "false")
self._assertNext('a', "false")
self._assertNext('l', "false")
self._assertNext('s', "false")
self._assertNext('e', "false")
return False
def _readNull(self):
self._assertNext('n', "null")
self._assertNext('u', "null")
self._assertNext('l', "null")
self._assertNext('l', "null")
return None
def _assertNext(self, ch, target):
if self._next() != ch:
raise ReadException, "Trying to read %s: '%s'" % (target, self._generator.all())
def _readNumber(self):
isfloat = False
result = self._next()
peek = self._peek()
while peek is not None and (peek.isdigit() or peek == "."):
isfloat = isfloat or peek == "."
result = result + self._next()
peek = self._peek()
try:
if isfloat:
return float(result)
else:
return int(result)
except ValueError:
raise ReadException, "Not a valid JSON number: '%s'" % result
def _readString(self):
result = ""
assert self._next() == '"'
try:
while self._peek() != '"':
ch = self._next()
if ch == "\\":
ch = self._next()
if ch in 'brnft':
ch = self.escapes[ch]
elif ch == "u":
ch4096 = self._next()
ch256 = self._next()
ch16 = self._next()
ch1 = self._next()
n = 4096 * self._hexDigitToInt(ch4096)
n += 256 * self._hexDigitToInt(ch256)
n += 16 * self._hexDigitToInt(ch16)
n += self._hexDigitToInt(ch1)
ch = unichr(n)
elif ch not in '"/\\':
raise ReadException, "Not a valid escaped JSON character: '%s' in %s" % (ch, self._generator.all())
result = result + ch
except StopIteration:
raise ReadException, "Not a valid JSON string: '%s'" % self._generator.all()
assert self._next() == '"'
return result
def _hexDigitToInt(self, ch):
try:
result = self.hex_digits[ch.upper()]
except KeyError:
try:
result = int(ch)
except ValueError:
raise ReadException, "The character %s is not a hex digit." % ch
return result
def _readComment(self):
assert self._next() == "/"
second = self._next()
if second == "/":
self._readDoubleSolidusComment()
elif second == '*':
self._readCStyleComment()
else:
raise ReadException, "Not a valid JSON comment: %s" % self._generator.all()
def _readCStyleComment(self):
try:
done = False
while not done:
ch = self._next()
done = (ch == "*" and self._peek() == "/")
if not done and ch == "/" and self._peek() == "*":
raise ReadException, "Not a valid JSON comment: %s, '/*' cannot be embedded in the comment." % self._generator.all()
self._next()
except StopIteration:
raise ReadException, "Not a valid JSON comment: %s, expected */" % self._generator.all()
def _readDoubleSolidusComment(self):
try:
ch = self._next()
while ch != "\r" and ch != "\n":
ch = self._next()
except StopIteration:
pass
def _readArray(self):
result = []
assert self._next() == '['
done = self._peek() == ']'
while not done:
item = self._read()
result.append(item)
self._eatWhitespace()
done = self._peek() == ']'
if not done:
ch = self._next()
if ch != ",":
raise ReadException, "Not a valid JSON array: '%s' due to: '%s'" % (self._generator.all(), ch)
assert ']' == self._next()
return result
def _readObject(self):
result = {}
assert self._next() == '{'
done = self._peek() == '}'
while not done:
key = self._read()
if type(key) is not types.StringType:
raise ReadException, "Not a valid JSON object key (should be a string): %s" % key
self._eatWhitespace()
ch = self._next()
if ch != ":":
raise ReadException, "Not a valid JSON object: '%s' due to: '%s'" % (self._generator.all(), ch)
self._eatWhitespace()
val = self._read()
result[key] = val
self._eatWhitespace()
done = self._peek() == '}'
if not done:
ch = self._next()
if ch != ",":
raise ReadException, "Not a valid JSON array: '%s' due to: '%s'" % (self._generator.all(), ch)
assert self._next() == "}"
return result
def _eatWhitespace(self):
p = self._peek()
while p is not None and p in string.whitespace or p == '/':
if p == '/':
self._readComment()
else:
self._next()
p = self._peek()
def _peek(self):
return self._generator.peek()
def _next(self):
return self._generator.next()
class JsonWriter(object):
def _append(self, s):
self._results.append(s)
def write(self, obj, escaped_forward_slash=False):
self._escaped_forward_slash = escaped_forward_slash
self._results = []
self._write(obj)
return "".join(self._results)
def _write(self, obj):
ty = type(obj)
if ty is types.DictType:
n = len(obj)
self._append("{")
for k, v in obj.items():
self._write(k)
self._append(":")
self._write(v)
n = n - 1
if n > 0:
self._append(",")
self._append("}")
elif ty is types.ListType or ty is types.TupleType:
n = len(obj)
self._append("[")
for item in obj:
self._write(item)
n = n - 1
if n > 0:
self._append(",")
self._append("]")
elif ty is types.StringType or ty is types.UnicodeType:
self._append('"')
obj = obj.replace('\\', r'\\')
if self._escaped_forward_slash:
obj = obj.replace('/', r'\/')
obj = obj.replace('"', r'\"')
obj = obj.replace('\b', r'\b')
obj = obj.replace('\f', r'\f')
obj = obj.replace('\n', r'\n')
obj = obj.replace('\r', r'\r')
obj = obj.replace('\t', r'\t')
self._append(obj)
self._append('"')
elif ty is types.IntType or ty is types.LongType:
self._append(str(obj))
elif ty is types.FloatType:
self._append("%f" % obj)
elif obj is True:
self._append("true")
elif obj is False:
self._append("false")
elif obj is None:
self._append("null")
else:
raise WriteException, "Cannot write in JSON: %s" % repr(obj)
def write(obj, escaped_forward_slash=False):
return JsonWriter().write(obj, escaped_forward_slash)
def read(s):
return JsonReader().read(s)

View File

@ -7,8 +7,7 @@ import urllib
import logging
import telnetlib
from util import json
import json
import os
@ -26,7 +25,7 @@ class Status:
# lookup OBP version
try:
response = urllib.urlopen(self.status_url)
response_json = json.read(response.read())
response_json = json.loads(response.read())
obp_version = int(response_json['version'])
logger.debug("OBP Version %s detected", obp_version)