Initial deploy

This commit is contained in:
Fergal Moran
2015-10-05 20:14:33 +01:00
parent 3ad11b011d
commit 7e6db3bf61
10 changed files with 177 additions and 204 deletions

2
.gitignore vendored
View File

@@ -91,3 +91,5 @@ docs/_build/
# PyBuilder
target/
config.ini

View File

@@ -1,24 +1,24 @@
FROM ubuntu:wily
MAINTAINER Fergal Moran "Ferg@lMoran.me"
ENV DEBIAN_FRONTEND noninteractive
FROM ubuntu:latest
MAINTAINER Fergal Moran <Ferg@lMoran.me>
RUN mkdir /code
RUN apt-get update -y
RUN apt-get upgrade -y
RUN apt-get -qq -y update && \
apt-get -qq -y install icecast2 python-setuptools python-pip pkg-config git \
libcurl4-openssl-dev libshout3 libshout3-dev && \
apt-get clean
RUN easy_install supervisor && \
easy_install supervisor-stdout
RUN apt-get install -y python-setuptools python-pip git pkg-config libshout3 libshout3-dev python-dev
RUN mkdir /code/
WORKDIR /code
ADD requirements.txt /code/
ADD server.py /code/
ADD ice_relay.py /code/
ADD static /code/static/
ADD templates /code/templates/
ADD dss.radio.conf /code/
ADD icecast2/icecast.xml /etc/icecast2/
ADD default/icecast2 /etc/default/
ADD supervisord.conf /etc/supervisord.conf
# Install tornado
RUN pip install -r requirements.txt
RUN pip install git+https://github.com/fergalmoran/python-shout.git#python-shout --upgrade
EXPOSE 8888
CMD ["python", "server.py"]

View File

@@ -1,19 +0,0 @@
# Defaults for icecast2 initscript
# sourced by /etc/init.d/icecast2
# installed at /etc/default/icecast2 by the maintainer scripts
#
# This is a POSIX shell fragment
#
# Full path to the server configuration file
CONFIGFILE="/etc/icecast2/icecast.xml"
# Name or ID of the user and group the daemon should run under
USERID=icecast2
GROUPID=icecast
# Edit /etc/icecast2/icecast.xml and change at least the passwords.
# Change this to true when done to enable the init.d script
ENABLE=true

View File

@@ -1,36 +0,0 @@
#!/usr/bin/env python
import shout
class IceClient(object):
def __init__(self, host, port, user, password, mount, format, protocol):
self.s = shout.Shout()
print "Using libshout version {}".format(shout.version())
self.s.host = host
self.s.port = port
self.s.user = user
self.s.password = password
self.s.mount = mount
self.s.format = format
self.s.protocol = protocol
self.s.open()
def stop(self):
self.s.close()
"""
if __name__ == '__main__':
streamer = IceClient(
host='niles',
port=8999,
user='source',
password='hackme',
mount="/pyshout",
format='mp3',
protocol='http'
)
streamer.play_audio(['/home/fergalm/Dropbox/BT_The_Moment_of_Truth.mp3'])
"""

View File

@@ -1,61 +1,155 @@
import logging
from threading import Thread
import time
from deefuzzer import Player
import urllib
import shout
import urllib2
import requests
BUF_LEN = 4096
class IceRelay(Thread):
def __init__(self, client, title='DeepSouthSounds Radio'):
server_ping = False
def __init__(self, options, mountpoint='dss', title='DeepSouthSounds Radio'):
super(IceRelay, self).__init__()
self.title = title
self.s = client
self._ended = True
self.player = Player("icecast")
self.isOpen = True
self.audio_queue = []
self.audio_index = 0
self.default_queue = [
'https://dsscdn.blob.core.windows.net/mixes/7568d3a4-9a9f-4f0f-a900-f84231c26c47.mp3'
]
self.channelIsOpen = False
def stop(self):
self.options = options
self.channel = shout.Shout()
self.channel.mount = '/' + mountpoint
self.api_host = options['api_host']
self.channel.url = 'http://deepsouthsounds.com/'
self.channel.name = title
self.channel.genre = 'Deep House Music'
self.channel.description = 'Deep sounds from the deep south'
self.channel.format = options['ice_format']
self.channel.host = options['ice_host']
self.channel.port = int(options['ice_port'])
self.channel.user = options['ice_user']
self.channel.password = options['ice_password']
self.channel.public = 1
if self.channel.format == 'mp3':
self.channel.audio_info = {
'bitrate': str(320),
'samplerate': str(48000),
'channels': str(2),
}
self.server_url = 'http://' + self.channel.host + ':' + str(self.channel.port) + self.channel.mount
print(self.server_url)
def channel_open(self):
if self.channelIsOpen:
return True
try:
self.channel.open()
self.channelIsOpen = True
return True
except Exception as ex:
logging.error('channel could not be opened: {}'.format(ex))
return False
def channel_close(self):
self.channelIsOpen = False
self._ended = True
try:
self.channel.close()
logging.info('channel closed')
except Exception as ex:
logging.error('channel could not be closed: {}'.format(ex))
def ping_server(self):
log = True
while not self.server_ping:
try:
server = urllib.urlopen(self.server_url)
self.server_ping = True
logging.info('Channel available.')
except:
time.sleep(1)
if log:
logging.error('Could not connect the channel. Waiting for channel to become available.')
log = False
def default_queue(self):
try:
r = requests.get('http://{}/mix/?random=True&limit=1'.format(self.api_host)) \
.json().get('results')[0].get('slug')
r = requests.get('http://{}/mix/{}/stream_url'.format(self.api_host, r))
url = r.json()['url']
return [
url
]
except Exception as ex:
logging.error(ex)
return [
'https://dsscdn.blob.core.windows.net/mixes/52df41af-5f81-4f00-a9a8-9ffb5dc3185f.mp3'
]
def set_audio_queue(self, queue):
self.audio_queue = queue
self._ended = True
def get_next_play_item(self):
print "Finding next item"
self._ended = False
try:
logging.debug("Finding next item")
# get random item from DSS api
if len(self.audio_queue) > self.audio_index:
item = self.audio_queue[self.audio_index]
else:
item = self.default_queue[0]
item = self.default_queue()[0]
self.player.set_media(item)
print "Playing: {}".format(item)
return self.player.file_read_remote()
logging.debug("Playing: {}".format(item))
self.stream = self.file_read_remote(item)
def close_channel(self):
self.isOpen = False
self._ended = False
return True
except Exception as ex:
logging.error('Error getting next play item: {}'.format(ex))
return False
def run(self):
self.ping_server()
while True:
now_playing = self.get_next_play_item()
if now_playing is not None:
for chunk in now_playing:
for self.chunk in self.stream:
try:
self.s.s.send(chunk)
self.s.s.sync()
self.channel.send(self.chunk)
self.channel.sync()
except Exception as ex:
print ("Error sending chunk: {0}".format(ex))
self.close_channel()
logging.error("Error sending chunk: {0}".format(ex))
self.channel_close()
if self._ended:
break
else:
print("No audio, waiting")
logging.debug("No audio, waiting")
time.sleep(5)
print "Outta here........"
def file_read_remote(self, item):
"""Read remote file and stream data through a generator."""
main_buffer_size = 0x10000
m = urllib2.urlopen(item)
while True:
__main_chunk = m.read(main_buffer_size)
if not __main_chunk:
break
yield __main_chunk
m.close()

View File

@@ -1,51 +0,0 @@
<icecast>
<location>Cork Like</location>
<admin>icemaster@deepsouthsounds.com</admin>
<limits>
<clients>100</clients>
<sources>2</sources>
<threadpool>5</threadpool>
<queue-size>524288</queue-size>
<client-timeout>30</client-timeout>
<header-timeout>15</header-timeout>
<source-timeout>10</source-timeout>
<burst-on-connect>1</burst-on-connect>
<burst-size>65535</burst-size>
</limits>
<authentication>
<source-password>RDzNlgqmj67vk</source-password>
<relay-password>9PmUbI1mLne9o</relay-password>
<admin-user>admin</admin-user>
<admin-password>CrVuP5evoJZ0.</admin-password>
</authentication>
<hostname>radio.deepsouthsounds.com</hostname>
<listen-socket>
<port>8351</port>
<shoutcast-mount>/dss</shoutcast-mount>
</listen-socket>
<fileserve>1</fileserve>
<paths>
<basedir>/usr/share/icecast2</basedir>
<logdir>/var/log/icecast2</logdir>
<webroot>/usr/share/icecast2/web</webroot>
<adminroot>/usr/share/icecast2/admin</adminroot>
<alias source="/" destination="/status.xsl"/>
</paths>
<logging>
<accesslog>access.log</accesslog>
<errorlog>error.log</errorlog>
<loglevel>3</loglevel>
<logsize>10000</logsize>
</logging>
<security>
<chroot>0</chroot>
</security>
</icecast>

View File

@@ -1,7 +1,5 @@
twisted
cython
requests
tornado==4.2.1
python-shout==0.2.1
git+https://github.com/fergalmoran/DeeFuzzer.git
git+https://github.com/fergalmoran/python-shout.git#python-shout

View File

@@ -1,28 +1,16 @@
#!/usr/bin/env python
import logging
from shout import ShoutException
import os
import signal
import time
import tornado
import tornado.ioloop
import tornado.web
import tornado.escape
from tornado.options import define, options, parse_command_line
import os
import signal
from ice_client import IceClient
from ice_relay import IceRelay
define("port", default=8888, help="run on the given port", type=int)
define("debug", default=True, help="run in debug mode")
define("ice_host", default='localhost', help="Icecast server host")
define("ice_port", default=8999, help="Icecast server port")
define("ice_user", default='source', help="Icecast user")
define("ice_password", default='hackme', help="Icecast password")
define("ice_mount", default='/mp3', help="Default icecast mount point")
define("ice_format", default='mp3', help="Format of the icecast server (mp3, vorbis, flac)")
define("ice_protocol", default='http', help="Protocol (currently only http)")
is_closing = False
class MainHandler(tornado.web.RequestHandler):
@@ -37,7 +25,6 @@ class PlayAudioHandler(tornado.web.RequestHandler):
in_file = data.get('audio_file')
if in_file is not None:
relay.set_audio_queue([in_file])
relay.stop()
time.sleep(10)
except Exception, ex:
raise tornado.web.HTTPError(500, ex.message)
@@ -46,12 +33,10 @@ class PlayAudioHandler(tornado.web.RequestHandler):
class StopAudioHandler(tornado.web.RequestHandler):
def post(self, *args, **kwargs):
try:
relay.stop()
relay.channel_close()
except Exception, ex:
raise tornado.web.HTTPError(500, ex.message)
is_closing = False
def signal_handler(signum, frame):
global is_closing
@@ -63,14 +48,35 @@ def try_exit():
global is_closing
if is_closing:
# clean up here
streamer.stop()
relay.stop()
relay.channel_close()
tornado.ioloop.IOLoop.instance().stop()
logging.info('exit success')
define("port", default=8888, help="run on the given port", type=int)
define("debug", default=True, help="run in debug mode")
define("ice_host", default='localhost', help="Icecast server host")
define("ice_port", default=8000, help="Icecast server port")
define("ice_user", default='source', help="Icecast user")
define("ice_password", default='hackme', help="Icecast password")
define("ice_mount", default='/mp3', help="Default icecast mount point")
define("ice_format", default='mp3', help="Format of the icecast server (mp3, vorbis, flac)")
define("ice_protocol", default='http', help="Protocol (currently only http)")
define("api_host", default='api.deepsouthsounds.com', help="API Host for serving audio")
#tornado.options.parse_command_line()
tornado.options.parse_config_file("dss.radio.conf")
relay = IceRelay(options=options)
def main():
if relay.channel_open():
relay.start()
else:
logging.error("IceCast relay failed to start")
exit()
app = tornado.web.Application(
[
(r"/", MainHandler),
@@ -81,30 +87,13 @@ def main():
template_path=os.path.join(os.path.dirname(__file__), "templates"),
static_path=os.path.join(os.path.dirname(__file__), "static"),
# xsrf_cookies=True,
debug=options.debug
debug=options['debug']
)
signal.signal(signal.SIGINT, signal_handler)
app.listen(options.port)
app.listen(options['port'])
tornado.ioloop.PeriodicCallback(try_exit, 100).start()
tornado.ioloop.IOLoop.current().start()
parse_command_line()
try:
streamer = IceClient(
host=options['ice_host'],
port=options['ice_port'],
user=options['ice_user'],
password=options['ice_password'],
mount=options['ice_mount'],
format=options['ice_format'],
protocol=options['ice_protocol']
)
relay = IceRelay(client=streamer)
relay.start()
except ShoutException as ex:
logging.info("Unable to connect to shout server {}:{} - {}".format(options['ice_host'], options['ice_port'], ex))
exit()
if __name__ == '__main__':
main()

View File

@@ -6,14 +6,14 @@ function getCookie(name) {
$(document).ready(function () {
var xsrf = getCookie("_xsrf");
$('#play_audio').click(function () {
$.post("/a/play", {
$.post("/a/play", JSON.stringify({
_xsrf: xsrf,
audio_file: $('#audio_file').val()
});
}), 'json');
});
$('#stop_audio').click(function () {
$.post("/a/stop", {
_xsrf: xsrf
});
}, 'json');
});
});

View File

@@ -18,16 +18,12 @@ supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock
[program:icecast2]
user=icecast2
command=icecast2 -n -c /etc/icecast2/icecast.xml
stopsignal=6
stdout_events_enabled=true
stderr_events_enabled=true
autorestart=true
[program:server]
command=server.py --ice_host=radio.deepsouthsounds.com --ice_port=8351 --ice_user=source --ice_password=RDzNlgqmj67vk --ice_mount=/dss
command=server.py --ice_host=radio.deepsouthsounds.com \
--ice_port=8351 \
--ice_user=source \
--ice_password=RDzNlgqmj67vk \
--ice_mount=/dss
stopsignal=6
stdout_events_enabled=true
stderr_events_enabled=true