PageRenderTime 76ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/etc/streamtheworld.py

https://github.com/emacsmirror/emacspeak
Python | 108 lines | 104 code | 2 blank | 2 comment | 1 complexity | 9e74a72a6509682251044036071035ff MD5 | raw file
Possible License(s): MIT
  1. #!/usr/bin/env python
  2. from random import choice
  3. import os
  4. import sys
  5. import urllib2
  6. import xml.dom.minidom as minidom
  7. def validate_callsign(cs):
  8. '''
  9. Normal callsign format is 'WWWWFFAAA', where 'WWWW' is the radio station
  10. callsign, 'FF' is either 'AM' or 'FM', and 'AAA' is always 'AAC'.
  11. For this function, we expect the 'WWWWFF' part as input.
  12. '''
  13. if not cs or not isinstance(cs, str):
  14. raise ValueError('callsign \'%s\' is not a string.' % cs)
  15. if len(cs) < 6:
  16. raise ValueError('callsign \'%s\' is too short.' % cs)
  17. if not cs.endswith('AAC'):
  18. cs = cs + 'AAC'
  19. band = cs[-5:-3]
  20. if band != 'AM' and band != 'FM':
  21. raise ValueError('callsign \'%s\' is missing \'FM\' or \'AM\'.' % cs)
  22. return cs
  23. def make_request(callsign):
  24. host = 'playerservices.streamtheworld.com'
  25. req = urllib2.Request(
  26. 'http://%s/api/livestream?version=1.2&mount=%s&lang=en' %
  27. (host, callsign))
  28. req.add_header('User-Agent', 'Mozilla/5.0')
  29. return req
  30. ## Example XML document we are parsing follows, as the minidom code is so beautiful to follow
  31. #
  32. #<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  33. #<live_stream_config version="1" xmlns="http://provisioning.streamtheworld.com/player/livestream-1.2">
  34. # <mountpoints>
  35. # <mountpoint>
  36. # <status>
  37. # <status-code>200</status-code>
  38. # <status-message>OK</status-message>
  39. # </status>
  40. # <servers>
  41. # <server sid="5203">
  42. # <ip>77.67.109.167</ip>
  43. # <ports>
  44. # <port>80</port>
  45. # <port>443</port>
  46. # <port>3690</port>
  47. # </ports>
  48. # </server>
  49. # <!-- multiple server elements usually present -->
  50. # </servers>
  51. # <mount>WXYTFMAAC</mount>
  52. # <format>FLV</format>
  53. # <bitrate>64000</bitrate>
  54. # <authentication>0</authentication>
  55. # <timeout>0</timeout>
  56. # </mountpoint>
  57. # </mountpoints>
  58. #</live_stream_config>
  59. def t(element):
  60. '''get the text of a DOM element'''
  61. return element.firstChild.data
  62. def check_status(ele):
  63. # should only be one status element inside a mountpoint
  64. status = ele.getElementsByTagName('status')[0]
  65. if t(status.getElementsByTagName('status-code')[0]) != '200':
  66. msg = t(status.getElementsByTagName('status-message')[0])
  67. raise Exception('Error locating stream: ' + msg)
  68. def create_stream_urls(srcfile):
  69. doc = minidom.parse(srcfile)
  70. mp = doc.getElementsByTagName('mountpoint')[0]
  71. check_status(mp)
  72. mt = t(mp.getElementsByTagName('mount')[0])
  73. allurls = []
  74. for s in mp.getElementsByTagName('server'):
  75. # a thing of beauty, right?
  76. ip = t(s.getElementsByTagName('ip')[0])
  77. ports = [t(p) for p in s.getElementsByTagName('port')]
  78. # yes, it is always HTTP. We see ports 80, 443, and 3690 usually
  79. urls = ['http://%s:%s/%s' % (ip, p, mt) for p in ports]
  80. allurls.extend(urls)
  81. return allurls
  82. def start_mplayer(location):
  83. return os.system('mplayer %s' % location)
  84. if __name__ == '__main__':
  85. if len(sys.argv) < 2:
  86. print 'usage: station callsign must be the first argument'
  87. sys.exit(1)
  88. callsign = validate_callsign(sys.argv[1])
  89. req = make_request(callsign)
  90. result = urllib2.urlopen(req)
  91. urls = create_stream_urls(result)
  92. if len(urls) > 0:
  93. u = choice(urls)
  94. sys.exit(start_mplayer(u))
  95. sys.exit(1)