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.

177 lines
5.5 KiB

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