You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

176 lines
5.4 KiB

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