lirva

streamtheworld.py

Mar 25th, 2012
123
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.84 KB | None | 0 0
  1. cat tunein-xbmc/resources/lib/streamtheworld.py
  2. #/*
  3. # *
  4. # * TuneIn Radio: TuneIn add-on for XBMC.
  5. # *
  6. # * Copyright (C) 2012 Diego Fernando Nieto
  7. # *
  8. # * This program is free software: you can redistribute it and/or modify
  9. # * it under the terms of the GNU General Public License as published by
  10. # * the Free Software Foundation, either version 3 of the License, or
  11. # * (at your option) any later version.
  12. # *
  13. # * This program is distributed in the hope that it will be useful,
  14. # * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # * GNU General Public License for more details.
  17. # *
  18. # * You should have received a copy of the GNU General Public License
  19. # * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. # *
  21. # */
  22.  
  23. from random import choice as choise
  24. import os
  25. import sys
  26. import urllib2
  27. import xml.dom.minidom as minidom
  28.  
  29.  
  30. class StreamTheWorld:
  31.  
  32.  
  33. ## Example XML document we are parsing follows, as the minidom code is so beautiful to follow
  34. # http://playerservices.streamtheworld.com/api/livestream?version=1.4&mount=CARACOL_RADIOAAC&lang=EN
  35. #
  36. #<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  37. #<live_stream_config version="1.4">
  38. # <mountpoints>
  39. # <mountpoint>
  40. # <status>
  41. # <status-code>200</status-code>
  42. # <status-message>OK</status-message>
  43. # </status>
  44. #
  45. # <transports>
  46. # <transport>http</transport>
  47. # </transports>
  48. #
  49. # <servers>
  50. # <server sid="3653">
  51. # <ip>3653.live.streamtheworld.com</ip>
  52. # <ports>
  53. # <port type="http">80</port>
  54. # <port type="http">3690</port>
  55. # <port type="http">443</port>
  56. # </ports>
  57. # </server>
  58. #
  59. # <server sid="1351">
  60. # <ip>1351.live.streamtheworld.com</ip>
  61. # <ports>
  62. # <port type="http">80</port>
  63. # <port type="http">3690</port>
  64. # <port type="http">443</port>
  65. # </ports>
  66. # </server>
  67. # </servers>
  68. #
  69. # <mount>CARACOL_RADIOAAC</mount>
  70. # <format>FLV</format>
  71. # <bitrate>32000</bitrate>
  72. # <media-format container="flv" cuepoints="andoxml">
  73. # <audio index="0" samplerate="44100" codec="heaacv2" bitrate="32000" channels="2"/>
  74. # </media-format>
  75. # <authentication>0</authentication>
  76. # <timeout>0</timeout>
  77. # </mountpoint>
  78. # </mountpoints>
  79. #</live_stream_config>
  80.  
  81. ''' Parse streamtheworld URL to HTTP Stream
  82. '''
  83. def __init__(self, cs):
  84. self.__cs__ = cs
  85. return
  86.  
  87.  
  88. def __validate_callsign(self, cs):
  89. '''
  90. Normal callsign format is 'WWWWAAA', where 'WWWW' is the radio station
  91. callsign and 'AAA' is always 'AAC'.
  92. '''
  93. if not cs or not isinstance(cs, str):
  94. raise ValueError('callsign \'%s\' is not a string.' % cs)
  95. if len(cs) < 6:
  96. raise ValueError('callsign \'%s\' is too short.' % cs)
  97. if not cs.endswith('AAC'):
  98. cs = cs + 'AAC'
  99. return cs
  100.  
  101. def __make_request(self, callsign):
  102. ''' Make a Call to StreamTheWorld API v1.4
  103. '''
  104. host = 'playerservices.streamtheworld.com'
  105. req = urllib2.Request(
  106. 'http://%s/api/livestream?version=1.4&mount=%s&lang=en' %
  107. (host, callsign))
  108.  
  109. req.add_header('User-Agent', 'Mozilla/5.0')
  110. return req
  111.  
  112. def __t(self, element):
  113. '''get the text of a DOM element'''
  114. return element.firstChild.data
  115.  
  116. def __check_status(self, ele):
  117. ''' should only be one status element inside a mountpoint
  118. '''
  119. status = ele.getElementsByTagName('status')[0]
  120. if self.__t(status.getElementsByTagName('status-code')[0]) != '200':
  121. msg = self.__t(status.getElementsByTagName('status-message')[0])
  122. raise Exception('Error locating stream: ' + msg)
  123.  
  124. def __create_stream_urls(self, srcfile):
  125. ''' Return an array with all URLs
  126. '''
  127. doc = minidom.parse(srcfile)
  128. mp = doc.getElementsByTagName('mountpoint')[0]
  129. self.__check_status(mp)
  130. mt = self.__t(mp.getElementsByTagName('mount')[0])
  131. allurls = []
  132. for s in mp.getElementsByTagName('server'):
  133. # a thing of beauty, right?
  134. ip = self.__t(s.getElementsByTagName('ip')[0])
  135. ports = [self.__t(p) for p in s.getElementsByTagName('port')]
  136. # yes, it is always HTTP. We see ports 80, 443, and 3690 usually
  137. urls = ['http://%s:%s/%s' % (ip, p, mt) for p in ports]
  138. allurls.extend(urls)
  139.  
  140. return allurls
  141.  
  142. def get_stream_url(self, cs):
  143. ''' Get one URL from CS
  144. '''
  145. callsign = self.__validate_callsign(cs)
  146. req = self.__make_request(callsign)
  147. result = urllib2.urlopen(req)
  148. urls = self.__create_stream_urls(result)
  149.  
  150. if len(urls) > 0:
  151. u = choise(urls)
  152. if not u.endswith('_SC'):
  153. u = u + '_SC'
  154. return u
  155.  
  156. '''if __name__ == '__main__':
  157. if len(sys.argv) < 2:
  158. print 'usage: station callsign must be the first argument'
  159. sys.exit(1)
  160.  
  161. __streamtheworld__ = StreamTheWorld(sys.argv[1])
  162. sys.exit(os.system('echo %s' % __streamtheworld__.get_stream_url(sys.argv[1])))
  163. sys.exit(1)
  164. '''
Add Comment
Please, Sign In to add comment