text: # Licensed under the MIT license
  # http://opensource.org/licenses/mit-license.php
  
  # an internet radio media server for the Coherence UPnP Framework
  # based on the radiotime (http://radiotime.com) catalog service
  
  # Copyright 2007, Frank Scholz <coherence@beebits.net>
  # Copyright 2009-2010, Jean-Michel Sizun <jmDOTsizunATfreeDOTfr>
  
  from twisted.internet import defer, reactor
  from twisted.python.failure import Failure
  from twisted.web import server
  
  from coherence.upnp.core import utils
  
  from coherence.upnp.core import DIDLLite
  from coherence.upnp.core.DIDLLite import classChooser, Resource, DIDLElement
  
  from coherence import log
  from coherence.backend import BackendItem, BackendStore, Container, LazyContainer, AbstractBackendStore
  #from coherence.backends.iradio_storage import PlaylistStreamProxy
  from urlparse import urlsplit
  from coherence.extern.et import parse_xml
  
  DEFAULT_NAME = 'Radiotime'
  DEFAULT_BROWSE_URL = 'http://opml.radiotime.com/Browse.ashx'
  DEFAULT_PARTNER_ID = 'TMe3Cn6v'
  
  # we only handle mp3 audio streams for now
  DEFAULT_FORMAT = "mp3"
  DEFAULT_MIMETYPE = "audio/mpeg"
  
  # TODO : extend format handling using radiotime API
  
  class RadiotimeAudioItem(BackendItem):
      logCategory = 'radiotime'
  
      def __init__(self, outline):
          BackendItem.__init__(self)
          self.preset_id = outline.get('preset_id')
          self.name = outline.get('text')
          self.mimetype = DEFAULT_MIMETYPE
          self.stream_url = outline.get('URL')
          self.image = outline.get('image')
          #self.location = PlaylistStreamProxy(self.stream_url)
          #self.url = self.stream_url
  
          self.item = None
  
      def replace_by (self, item):
          # do nothing: we suppose the replacement item is the same
          return
  
      def get_item(self):
          if self.item == None:
              upnp_id = self.get_id()
              upnp_parent_id = self.parent.get_id()
              self.item = DIDLLite.AudioBroadcast(upnp_id, upnp_parent_id, self.name)
              self.item.albumArtURI = self.image
              res = Resource(self.stream_url, 'http-get:*:%s:%s' % (self.mimetype,
                                                             ';'.join(('DLNA.ORG_PN=MP3',
                                                                       'DLNA.ORG_CI=0',
                                                                       'DLNA.ORG_OP=01',
                                                                       'DLNA.ORG_FLAGS=01700000000000000000000000000000'))))
              res.size = 0  # None
              self.item.res.append(res)
          return self.item
  
      def get_path(self):
          return self.url
  
      def get_id(self):
          return self.storage_id
  
      
  class RadiotimeStore(AbstractBackendStore):
  
      logCategory = 'radiotime'
  
      implements = ['MediaServer']
  
      def __init__(self, server, **kwargs):
          AbstractBackendStore.__init__(self, server, **kwargs)
  
          self.name = kwargs.get('name', DEFAULT_NAME)
          self.refresh = int(kwargs.get('refresh', 60)) * 60
  
          self.browse_url = self.config.get('browse_url', DEFAULT_BROWSE_URL)
          self.partner_id = self.config.get('partner_id', DEFAULT_PARTNER_ID)
          self.username = self.config.get('username', None)
          self.locale = self.config.get('locale', 'en')
          self.serial = server.uuid
  
          # construct URL for root menu
          if self.username is not None:
              identification_param = "username=%s" % self.username
          else:
              identification_param = "serial=%s" % self.serial
          formats_value = DEFAULT_FORMAT
          root_url = "%s?partnerId=%s&%s&formats=%s&locale=%s" % (self.browse_url, self.partner_id, identification_param, formats_value, self.locale)
  
          # set root item
          root_item = LazyContainer(None, "root", "root", self.refresh, self.retrieveItemsForOPML, url=root_url)
          self.set_root_item(root_item)
  
          self.init_completed()
  
      def upnp_init(self):
          self.current_connection_id = None
  
          self.wmc_mapping = {'4': self.get_root_id()}
  
          if self.server:
              self.server.connection_manager_server.set_variable(0, 'SourceProtocolInfo',
                                                                      ['http-get:*:audio/mpeg:*',
                                                                       'http-get:*:audio/x-scpls:*'],
                                                                       default=True)
  
      def retrieveItemsForOPML (self, parent, url):
  
          def append_outline (parent, outline):
              type = outline.get('type')
              if type is None:
                  # This outline is just a classification item containing other outline elements
                  # the corresponding item will a static Container
                  text = outline.get('text')
                  key = outline.get('key')
                  external_id = None
                  if external_id is None and key is not None:
                      external_id = "%s_%s" % (parent.external_id, key)
                  if external_id is None:
                      external_id = outline_url
                  item = Container(parent, text)
                  item.external_id = external_id
                  item.store = parent.store
                  parent.add_child(item, external_id=external_id)
                  sub_outlines = outline.findall('outline')
                  for sub_outline in sub_outlines:
                      append_outline(item, sub_outline)
  
              elif type in ('link'):
                  # the corresponding item will a self-populating Container
                  text = outline.get('text')
                  outline_url = outline.get('URL')
                  key = outline.get('key')
                  guide_id = outline.get('guide_id')
                  external_id = guide_id
                  if external_id is None and key is not None:
                      external_id = "%s_%s" % (parent.external_id, key)
                  if external_id is None:
                      external_id = outline_url
                  item = LazyContainer(parent, text, external_id, self.refresh, self.retrieveItemsForOPML, url=outline_url)
                  parent.add_child(item, external_id=external_id)
  
              elif type in ('audio'):
                  item = RadiotimeAudioItem(outline)
                  parent.add_child(item, external_id=item.preset_id)
  
          def got_page(result):
              self.info('connection to Radiotime service successful for url %s', url)
  
              outlines = result.findall('body/outline')
              for outline in outlines:
                  append_outline(parent, outline)
  
              return True
  
          def got_error(error):
              self.warning("connection to Radiotime service failed for url %s", url)
              self.debug("%r", error.getTraceback())
              parent.childrenRetrievingNeeded = True  # we retry
              return Failure("Unable to retrieve items for url %s" % url)
  
          def got_xml_error(error):
              self.warning("Data received from Radiotime service is invalid: %s", url)
              #self.debug("%r", error.getTraceback())
              print error.getTraceback()
              parent.childrenRetrievingNeeded = True  # we retry
              return Failure("Unable to retrieve items for url %s" % url)
  
          d = utils.getPage(url, )
          d.addCallback(parse_xml)
          d.addErrback(got_error)
          d.addCallback(got_page)
          d.addErrback(got_xml_error)
          return d
  