self.media_dir = self.station['media']['dir']
self.channel.format = self.station['media']['format']
self.mode_shuffle = int(self.station['media']['shuffle'])
+ self.bitrate = self.station['media']['bitrate']
+ self.ogg_quality = self.station['media']['ogg_quality']
+ self.samplerate = self.station['media']['samplerate']
+ self.voices = self.station['media']['voices']
# Infos
self.short_name = self.station['infos']['short_name']
self.channel.name = self.station['infos']['name']
self.channel.genre = self.station['infos']['genre']
self.channel.description = self.station['infos']['description']
self.channel.url = self.station['infos']['url']
+ self.rss_file = '/tmp/' + self.short_name + '.xml'
# Server
self.channel.protocol = 'http' # | 'xaudiocast' | 'icy'
self.channel.host = self.station['server']['host']
self.channel.password = self.station['server']['sourcepassword']
self.channel.mount = '/' + self.short_name + '.' + self.channel.format
self.channel.public = int(self.station['server']['public'])
- # s.audio_info = { 'key': 'val', ... }
- # (keys are shout.SHOUT_AI_BITRATE, shout.SHOUT_AI_SAMPLERATE,
- # shout.SHOUT_AI_CHANNELS, shout.SHOUT_AI_QUALITY)
+ self.channel.audio_info = { 'SHOUT_AI_BITRATE': self.bitrate,
+ 'SHOUT_AI_SAMPLERATE': self.samplerate,
+ 'SHOUT_AI_QUALITY': self.ogg_quality,
+ 'SHOUT_AI_CHANNELS': self.voices,
+ }
+ self.channel.open()
+ self.playlist = self.get_playlist()
+ self.lp = len(self.playlist)
+ self.rand_list = range(0,self.lp-1)
+ print 'Opening ' + self.short_name + ' - ' + self.channel.name + \
+ ' (' + str(self.lp) + ' tracks)...'
+ time.sleep(0.1)
+
+ def update_rss(self, file_name):
+ self.media_url_dir = '/media/'
+ rss = PyRSS2Gen.RSS2(
+ title = self.channel.name,
+ link = self.channel.url,
+ description = self.channel.description,
+ lastBuildDate = datetime.datetime.now(),
+
+ items = [
+ PyRSS2Gen.RSSItem(
+ title = file_name,
+ link = self.channel.url + self.media_url_dir + file_name,
+ description = file_name,
+ guid = PyRSS2Gen.Guid(self.channel.url + self.media_url_dir + file_name),
+ pubDate = datetime.datetime(2003, 9, 6, 21, 31)),
+ ])
+
+ rss.write_xml(open(self.rss_file, "w"))
def get_playlist(self):
file_list = []
index = self.rand_list[self.id]
return playlist, playlist[index]
+
def core_process(self, media, buffer_size):
"""Read media and stream data through a generator.
Taken from Telemeta (see http://telemeta.org)"""
yield __chunk
def run(self):
- print "Using libshout version %s" % shout.version()
+ #print "Using libshout version %s" % shout.version()
q = self.q
__chunk = 0
- playlist = self.get_playlist()
- lp = len(playlist)
- self.rand_list = range(0,lp-1)
- self.channel.open()
- print 'Opening ' + self.short_name + ' - ' + self.channel.name + \
- ' (' + str(lp) + ' tracks)...'
- time.sleep(0.1)
while True:
- if lp == 0:
+ if self.lp == 0:
break
if self.mode_shuffle == 1:
- playlist, media = self.get_next_media_rand(playlist)
+ self.playlist, media = self.get_next_media_rand(self.playlist)
else:
- playlist, media = self.get_next_media_lin(playlist)
+ self.playlist, media = self.get_next_media_lin(self.playlist)
self.counter += 1
if os.path.exists(media) and not '/.' in media:
self.channel.set_metadata({'song': file_name})
stream = self.core_process(media, self.buffer_size)
print 'Defuzzing this file on %s : id = %s, name = %s' % (self.short_name, self.id, file_name)
-
+ self.update_rss(file_name)
+
for __chunk in stream:
self.channel.send(__chunk)
self.channel.sync()
# Get the queue
it = q.get(1)
#print "Station eated one queue step: "+str(it)
- self.channel.close()
+
+ #self.channel.close()
def main():
--- /dev/null
+"""PyRSS2Gen - A Python library for generating RSS 2.0 feeds."""
+
+__name__ = "PyRSS2Gen"
+__version__ = (1, 0, 0)
+__author__ = "Andrew Dalke <dalke@dalkescientific.com>"
+
+_generator_name = __name__ + "-" + ".".join(map(str, __version__))
+
+import datetime
+
+# Could make this the base class; will need to add 'publish'
+class WriteXmlMixin:
+ def write_xml(self, outfile, encoding = "iso-8859-1"):
+ from xml.sax import saxutils
+ handler = saxutils.XMLGenerator(outfile, encoding)
+ handler.startDocument()
+ self.publish(handler)
+ handler.endDocument()
+
+ def to_xml(self, encoding = "iso-8859-1"):
+ try:
+ import cStringIO as StringIO
+ except ImportError:
+ import StringIO
+ f = StringIO.StringIO()
+ self.write_xml(f, encoding)
+ return f.getvalue()
+
+
+def _element(handler, name, obj, d = {}):
+ if isinstance(obj, basestring) or obj is None:
+ # special-case handling to make the API easier
+ # to use for the common case.
+ handler.startElement(name, d)
+ if obj is not None:
+ handler.characters(obj)
+ handler.endElement(name)
+ else:
+ # It better know how to emit the correct XML.
+ obj.publish(handler)
+
+def _opt_element(handler, name, obj):
+ if obj is None:
+ return
+ _element(handler, name, obj)
+
+
+def _format_date(dt):
+ """convert a datetime into an RFC 822 formatted date
+
+ Input date must be in GMT.
+ """
+ # Looks like:
+ # Sat, 07 Sep 2002 00:00:01 GMT
+ # Can't use strftime because that's locale dependent
+ #
+ # Isn't there a standard way to do this for Python? The
+ # rfc822 and email.Utils modules assume a timestamp. The
+ # following is based on the rfc822 module.
+ return "%s, %02d %s %04d %02d:%02d:%02d GMT" % (
+ ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][dt.weekday()],
+ dt.day,
+ ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][dt.month-1],
+ dt.year, dt.hour, dt.minute, dt.second)
+
+
+##
+# A couple simple wrapper objects for the fields which
+# take a simple value other than a string.
+class IntElement:
+ """implements the 'publish' API for integers
+
+ Takes the tag name and the integer value to publish.
+
+ (Could be used for anything which uses str() to be published
+ to text for XML.)
+ """
+ element_attrs = {}
+ def __init__(self, name, val):
+ self.name = name
+ self.val = val
+ def publish(self, handler):
+ handler.startElement(self.name, self.element_attrs)
+ handler.characters(str(self.val))
+ handler.endElement(self.name)
+
+class DateElement:
+ """implements the 'publish' API for a datetime.datetime
+
+ Takes the tag name and the datetime to publish.
+
+ Converts the datetime to RFC 2822 timestamp (4-digit year).
+ """
+ def __init__(self, name, dt):
+ self.name = name
+ self.dt = dt
+ def publish(self, handler):
+ _element(handler, self.name, _format_date(self.dt))
+####
+
+class Category:
+ """Publish a category element"""
+ def __init__(self, category, domain = None):
+ self.category = category
+ self.domain = domain
+ def publish(self, handler):
+ d = {}
+ if self.domain is not None:
+ d["domain"] = self.domain
+ _element(handler, "category", self.category, d)
+
+class Cloud:
+ """Publish a cloud"""
+ def __init__(self, domain, port, path,
+ registerProcedure, protocol):
+ self.domain = domain
+ self.port = port
+ self.path = path
+ self.registerProcedure = registerProcedure
+ self.protocol = protocol
+ def publish(self, handler):
+ _element(handler, "cloud", None, {
+ "domain": self.domain,
+ "port": str(self.port),
+ "path": self.path,
+ "registerProcedure": self.registerProcedure,
+ "protocol": self.protocol})
+
+class Image:
+ """Publish a channel Image"""
+ element_attrs = {}
+ def __init__(self, url, title, link,
+ width = None, height = None, description = None):
+ self.url = url
+ self.title = title
+ self.link = link
+ self.width = width
+ self.height = height
+ self.description = description
+
+ def publish(self, handler):
+ handler.startElement("image", self.element_attrs)
+
+ _element(handler, "url", self.url)
+ _element(handler, "title", self.title)
+ _element(handler, "link", self.link)
+
+ width = self.width
+ if isinstance(width, int):
+ width = IntElement("width", width)
+ _opt_element(handler, "width", width)
+
+ height = self.height
+ if isinstance(height, int):
+ height = IntElement("height", height)
+ _opt_element(handler, "height", height)
+
+ _opt_element(handler, "description", self.description)
+
+ handler.endElement("image")
+
+class Guid:
+ """Publish a guid
+
+ Defaults to being a permalink, which is the assumption if it's
+ omitted. Hence strings are always permalinks.
+ """
+ def __init__(self, guid, isPermaLink = 1):
+ self.guid = guid
+ self.isPermaLink = isPermaLink
+ def publish(self, handler):
+ d = {}
+ if self.isPermaLink:
+ d["isPermaLink"] = "true"
+ else:
+ d["isPermaLink"] = "false"
+ _element(handler, "guid", self.guid, d)
+
+class TextInput:
+ """Publish a textInput
+
+ Apparently this is rarely used.
+ """
+ element_attrs = {}
+ def __init__(self, title, description, name, link):
+ self.title = title
+ self.description = description
+ self.name = name
+ self.link = link
+
+ def publish(self, handler):
+ handler.startElement("textInput", self.element_attrs)
+ _element(handler, "title", self.title)
+ _element(handler, "description", self.description)
+ _element(handler, "name", self.name)
+ _element(handler, "link", self.link)
+ handler.endElement("textInput")
+
+
+class Enclosure:
+ """Publish an enclosure"""
+ def __init__(self, url, length, type):
+ self.url = url
+ self.length = length
+ self.type = type
+ def publish(self, handler):
+ _element(handler, "enclosure", None,
+ {"url": self.url,
+ "length": str(self.length),
+ "type": self.type,
+ })
+
+class Source:
+ """Publish the item's original source, used by aggregators"""
+ def __init__(self, name, url):
+ self.name = name
+ self.url = url
+ def publish(self, handler):
+ _element(handler, "source", self.name, {"url": self.url})
+
+class SkipHours:
+ """Publish the skipHours
+
+ This takes a list of hours, as integers.
+ """
+ element_attrs = {}
+ def __init__(self, hours):
+ self.hours = hours
+ def publish(self, handler):
+ if self.hours:
+ handler.startElement("skipHours", self.element_attrs)
+ for hour in self.hours:
+ _element(handler, "hour", str(hour))
+ handler.endElement("skipHours")
+
+class SkipDays:
+ """Publish the skipDays
+
+ This takes a list of days as strings.
+ """
+ element_attrs = {}
+ def __init__(self, days):
+ self.days = days
+ def publish(self, handler):
+ if self.days:
+ handler.startElement("skipDays", self.element_attrs)
+ for day in self.days:
+ _element(handler, "day", day)
+ handler.endElement("skipDays")
+
+class RSS2(WriteXmlMixin):
+ """The main RSS class.
+
+ Stores the channel attributes, with the "category" elements under
+ ".categories" and the RSS items under ".items".
+ """
+
+ rss_attrs = {"version": "2.0"}
+ element_attrs = {}
+ def __init__(self,
+ title,
+ link,
+ description,
+
+ language = None,
+ copyright = None,
+ managingEditor = None,
+ webMaster = None,
+ pubDate = None, # a datetime, *in* *GMT*
+ lastBuildDate = None, # a datetime
+
+ categories = None, # list of strings or Category
+ generator = _generator_name,
+ docs = "http://blogs.law.harvard.edu/tech/rss",
+ cloud = None, # a Cloud
+ ttl = None, # integer number of minutes
+
+ image = None, # an Image
+ rating = None, # a string; I don't know how it's used
+ textInput = None, # a TextInput
+ skipHours = None, # a SkipHours with a list of integers
+ skipDays = None, # a SkipDays with a list of strings
+
+ items = None, # list of RSSItems
+ ):
+ self.title = title
+ self.link = link
+ self.description = description
+ self.language = language
+ self.copyright = copyright
+ self.managingEditor = managingEditor
+
+ self.webMaster = webMaster
+ self.pubDate = pubDate
+ self.lastBuildDate = lastBuildDate
+
+ if categories is None:
+ categories = []
+ self.categories = categories
+ self.generator = generator
+ self.docs = docs
+ self.cloud = cloud
+ self.ttl = ttl
+ self.image = image
+ self.rating = rating
+ self.textInput = textInput
+ self.skipHours = skipHours
+ self.skipDays = skipDays
+
+ if items is None:
+ items = []
+ self.items = items
+
+ def publish(self, handler):
+ handler.startElement("rss", self.rss_attrs)
+ handler.startElement("channel", self.element_attrs)
+ _element(handler, "title", self.title)
+ _element(handler, "link", self.link)
+ _element(handler, "description", self.description)
+
+ self.publish_extensions(handler)
+
+ _opt_element(handler, "language", self.language)
+ _opt_element(handler, "copyright", self.copyright)
+ _opt_element(handler, "managingEditor", self.managingEditor)
+ _opt_element(handler, "webMaster", self.webMaster)
+
+ pubDate = self.pubDate
+ if isinstance(pubDate, datetime.datetime):
+ pubDate = DateElement("pubDate", pubDate)
+ _opt_element(handler, "pubDate", pubDate)
+
+ lastBuildDate = self.lastBuildDate
+ if isinstance(lastBuildDate, datetime.datetime):
+ lastBuildDate = DateElement("lastBuildDate", lastBuildDate)
+ _opt_element(handler, "lastBuildDate", lastBuildDate)
+
+ for category in self.categories:
+ if isinstance(category, basestring):
+ category = Category(category)
+ category.publish(handler)
+
+ _opt_element(handler, "generator", self.generator)
+ _opt_element(handler, "docs", self.docs)
+
+ if self.cloud is not None:
+ self.cloud.publish(handler)
+
+ ttl = self.ttl
+ if isinstance(self.ttl, int):
+ ttl = IntElement("ttl", ttl)
+ _opt_element(handler, "tt", ttl)
+
+ if self.image is not None:
+ self.image.publish(handler)
+
+ _opt_element(handler, "rating", self.rating)
+ if self.textInput is not None:
+ self.textInput.publish(handler)
+ if self.skipHours is not None:
+ self.skipHours.publish(handler)
+ if self.skipDays is not None:
+ self.skipDays.publish(handler)
+
+ for item in self.items:
+ item.publish(handler)
+
+ handler.endElement("channel")
+ handler.endElement("rss")
+
+ def publish_extensions(self, handler):
+ # Derived classes can hook into this to insert
+ # output after the three required fields.
+ pass
+
+
+
+class RSSItem(WriteXmlMixin):
+ """Publish an RSS Item"""
+ element_attrs = {}
+ def __init__(self,
+ title = None, # string
+ link = None, # url as string
+ description = None, # string
+ author = None, # email address as string
+ categories = None, # list of string or Category
+ comments = None, # url as string
+ enclosure = None, # an Enclosure
+ guid = None, # a unique string
+ pubDate = None, # a datetime
+ source = None, # a Source
+ ):
+
+ if title is None and description is None:
+ raise TypeError(
+ "must define at least one of 'title' or 'description'")
+ self.title = title
+ self.link = link
+ self.description = description
+ self.author = author
+ if categories is None:
+ categories = []
+ self.categories = categories
+ self.comments = comments
+ self.enclosure = enclosure
+ self.guid = guid
+ self.pubDate = pubDate
+ self.source = source
+ # It sure does get tedious typing these names three times...
+
+ def publish(self, handler):
+ handler.startElement("item", self.element_attrs)
+ _opt_element(handler, "title", self.title)
+ _opt_element(handler, "link", self.link)
+ self.publish_extensions(handler)
+ _opt_element(handler, "description", self.description)
+ _opt_element(handler, "author", self.author)
+
+ for category in self.categories:
+ if isinstance(category, basestring):
+ category = Category(category)
+ category.publish(handler)
+
+ _opt_element(handler, "comments", self.comments)
+ if self.enclosure is not None:
+ self.enclosure.publish(handler)
+ _opt_element(handler, "guid", self.guid)
+
+ pubDate = self.pubDate
+ if isinstance(pubDate, datetime.datetime):
+ pubDate = DateElement("pubDate", pubDate)
+ _opt_element(handler, "pubDate", pubDate)
+
+ if self.source is not None:
+ self.source.publish(handler)
+
+ handler.endElement("item")
+
+ def publish_extensions(self, handler):
+ # Derived classes can hook into this to insert
+ # output after the title and link elements
+ pass