tunein.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import re
  2. from .common import InfoExtractor
  3. from ..utils import ExtractorError
  4. from ..compat import compat_urlparse
  5. class TuneInBaseIE(InfoExtractor):
  6. _API_BASE_URL = 'http://tunein.com/tuner/tune/'
  7. def _real_extract(self, url):
  8. content_id = self._match_id(url)
  9. content_info = self._download_json(
  10. self._API_BASE_URL + self._API_URL_QUERY % content_id,
  11. content_id, note='Downloading JSON metadata')
  12. title = content_info['Title']
  13. thumbnail = content_info.get('Logo')
  14. location = content_info.get('Location')
  15. streams_url = content_info.get('StreamUrl')
  16. if not streams_url:
  17. raise ExtractorError('No downloadable streams found', expected=True)
  18. if not streams_url.startswith('http://'):
  19. streams_url = compat_urlparse.urljoin(url, streams_url)
  20. streams = self._download_json(
  21. streams_url, content_id, note='Downloading stream data',
  22. transform_source=lambda s: re.sub(r'^\s*\((.*)\);\s*$', r'\1', s))['Streams']
  23. is_live = None
  24. formats = []
  25. for stream in streams:
  26. if stream.get('Type') == 'Live':
  27. is_live = True
  28. reliability = stream.get('Reliability')
  29. format_note = (
  30. 'Reliability: %d%%' % reliability
  31. if reliability is not None else None)
  32. formats.append({
  33. 'preference': (
  34. 0 if reliability is None or reliability > 90
  35. else 1),
  36. 'abr': stream.get('Bandwidth'),
  37. 'ext': stream.get('MediaType').lower(),
  38. 'acodec': stream.get('MediaType'),
  39. 'vcodec': 'none',
  40. 'url': stream.get('Url'),
  41. 'source_preference': reliability,
  42. 'format_note': format_note,
  43. })
  44. return {
  45. 'id': content_id,
  46. 'title': title,
  47. 'formats': formats,
  48. 'thumbnail': thumbnail,
  49. 'location': location,
  50. 'is_live': is_live,
  51. }
  52. class TuneInClipIE(TuneInBaseIE):
  53. IE_NAME = 'tunein:clip'
  54. _VALID_URL = r'https?://(?:www\.)?tunein\.com/station/.*?audioClipId\=(?P<id>\d+)'
  55. _API_URL_QUERY = '?tuneType=AudioClip&audioclipId=%s'
  56. _TESTS = [{
  57. 'url': 'http://tunein.com/station/?stationId=246119&audioClipId=816',
  58. 'md5': '99f00d772db70efc804385c6b47f4e77',
  59. 'info_dict': {
  60. 'id': '816',
  61. 'title': '32m',
  62. 'ext': 'mp3',
  63. },
  64. }]
  65. class TuneInStationIE(TuneInBaseIE):
  66. IE_NAME = 'tunein:station'
  67. _VALID_URL = r'https?://(?:www\.)?tunein\.com/(?:radio/.*?-s|station/.*?StationId=|embed/player/s)(?P<id>\d+)'
  68. _EMBED_REGEX = [r'<iframe[^>]+src=["\'](?P<url>(?:https?://)?tunein\.com/embed/player/[pst]\d+)']
  69. _API_URL_QUERY = '?tuneType=Station&stationId=%s'
  70. @classmethod
  71. def suitable(cls, url):
  72. return False if TuneInClipIE.suitable(url) else super(TuneInStationIE, cls).suitable(url)
  73. _TESTS = [{
  74. 'url': 'http://tunein.com/radio/Jazz24-885-s34682/',
  75. 'info_dict': {
  76. 'id': '34682',
  77. 'title': 'Jazz 24 on 88.5 Jazz24 - KPLU-HD2',
  78. 'ext': 'mp3',
  79. 'location': 'Tacoma, WA',
  80. },
  81. 'params': {
  82. 'skip_download': True, # live stream
  83. },
  84. }, {
  85. 'url': 'http://tunein.com/embed/player/s6404/',
  86. 'only_matching': True,
  87. }]
  88. class TuneInProgramIE(TuneInBaseIE):
  89. IE_NAME = 'tunein:program'
  90. _VALID_URL = r'https?://(?:www\.)?tunein\.com/(?:radio/.*?-p|program/.*?ProgramId=|embed/player/p)(?P<id>\d+)'
  91. _API_URL_QUERY = '?tuneType=Program&programId=%s'
  92. _TESTS = [{
  93. 'url': 'http://tunein.com/radio/Jazz-24-p2506/',
  94. 'info_dict': {
  95. 'id': '2506',
  96. 'title': 'Jazz 24 on 91.3 WUKY-HD3',
  97. 'ext': 'mp3',
  98. 'location': 'Lexington, KY',
  99. },
  100. 'params': {
  101. 'skip_download': True, # live stream
  102. },
  103. }, {
  104. 'url': 'http://tunein.com/embed/player/p191660/',
  105. 'only_matching': True,
  106. }]
  107. class TuneInTopicIE(TuneInBaseIE):
  108. IE_NAME = 'tunein:topic'
  109. _VALID_URL = r'https?://(?:www\.)?tunein\.com/(?:topic/.*?TopicId=|embed/player/t)(?P<id>\d+)'
  110. _API_URL_QUERY = '?tuneType=Topic&topicId=%s'
  111. _TESTS = [{
  112. 'url': 'http://tunein.com/topic/?TopicId=101830576',
  113. 'md5': 'c31a39e6f988d188252eae7af0ef09c9',
  114. 'info_dict': {
  115. 'id': '101830576',
  116. 'title': 'Votez pour moi du 29 octobre 2015 (29/10/15)',
  117. 'ext': 'mp3',
  118. 'location': 'Belgium',
  119. },
  120. }, {
  121. 'url': 'http://tunein.com/embed/player/t101830576/',
  122. 'only_matching': True,
  123. }]
  124. class TuneInShortenerIE(InfoExtractor):
  125. IE_NAME = 'tunein:shortener'
  126. IE_DESC = False # Do not list
  127. _VALID_URL = r'https?://tun\.in/(?P<id>[A-Za-z0-9]+)'
  128. _TEST = {
  129. # test redirection
  130. 'url': 'http://tun.in/ser7s',
  131. 'info_dict': {
  132. 'id': '34682',
  133. 'title': 'Jazz 24 on 88.5 Jazz24 - KPLU-HD2',
  134. 'ext': 'mp3',
  135. 'location': 'Tacoma, WA',
  136. },
  137. 'params': {
  138. 'skip_download': True, # live stream
  139. },
  140. }
  141. def _real_extract(self, url):
  142. redirect_id = self._match_id(url)
  143. # The server doesn't support HEAD requests
  144. urlh = self._request_webpage(
  145. url, redirect_id, note='Downloading redirect page')
  146. url = urlh.geturl()
  147. self.to_screen('Following redirect: %s' % url)
  148. return self.url_result(url)