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