[younow] Add extractor
[youtube-dl] / youtube_dl / extractor / younow.py
1 # coding: utf-8
2 from __future__ import unicode_literals
3 from datetime import date, datetime
4
5 from .common import InfoExtractor
6 from ..compat import compat_str
7 from ..utils import int_or_none, UnsupportedError
8
9 MOMENT_URL_FORMAT = 'https://cdn.younow.com/php/api/moment/fetch/id=%s'
10 STREAM_URL_FORMAT = 'https://hls.younow.com/momentsplaylists/live/%s/%s.m3u8'
11
12
13 class YouNowIE(InfoExtractor):
14     _VALID_URL = r'https?://(?:www\.)?younow\.com/(?P<id>[^/]+)'
15     _TEST = {
16         'url': 'https://www.younow.com/AmandaPadeezy',
17         'info_dict': {
18             'id': 'AmandaPadeezy',
19             'ext': 'mp4',
20             'is_live': True,
21             'title': 'March 26, 2017',
22             'description': 'YouNow is the best way to broadcast live and get an audience to watch you.',
23             'thumbnail': 'https://ynassets.s3.amazonaws.com/broadcast/live/157869188/157869188.jpg',
24             'tags': ['girls'],
25             'categories': ['girls'],
26             'uploader': 'AmandaPadeezy',
27             'uploader_id': '6716501',
28             'uploader_url': 'https://www.younow.com/AmandaPadeezy',
29             'creator': 'AmandaPadeezy',
30             'formats': [{
31                 'url': 'https://cdn.younow.com/php/api/broadcast/videoPath/hls=1/broadcastId=157869188/channelId=6716501',
32                 'ext': 'mp4',
33                 'protocol': 'm3u8',
34             }],
35         }
36     }
37
38     def _real_extract(self, url):
39         username = self._match_id(url)
40         data = self._download_json('https://api.younow.com/php/api/broadcast/info/curId=0/user=%s' % (username), username)
41
42         if data.get('media'):
43             stream_url = 'https://cdn.younow.com/php/api/broadcast/videoPath/hls=1/broadcastId=%s/channelId=%s' % (
44                 data.get('broadcastId'),
45                 data.get('userId'),
46             )
47         else:
48             raise UnsupportedError('Unsupported stream or user is not streaming at this time')
49
50         webpage = self._download_webpage(url, username)
51         try:
52             uploader = data['user']['profileUrlString']
53         except KeyError:
54             uploader = username
55         try:
56             title = data['title']
57         except KeyError:
58             title = date.today().strftime('%B %d, %Y')
59
60         return {
61             'id': uploader,
62             'is_live': True,
63             'title': title,
64             'description': self._og_search_description(webpage),
65             'thumbnail': data.get('awsUrl'),
66             'tags': data.get('tags'),
67             'categories': data.get('tags'),
68             'uploader': uploader,
69             'uploader_id': data.get('userId'),
70             'uploader_url': 'https://www.younow.com/%s' % (data['user']['profileUrlString'],),
71             'creator': uploader,
72             'view_count': int_or_none(data.get('viewers')),
73             'like_count': int_or_none(data.get('likes')),
74             'formats': [{
75                 'url': stream_url,
76                 'ext': 'mp4',
77                 'protocol': 'm3u8',
78             }],
79         }
80
81
82 def _moment_to_entry(item):
83     title = item.get('text')
84     title_type = item.get('titleType')
85     if not title:
86         if title_type:
87             title = 'YouNow %s' % item.get('titleType')
88         else:
89             title = 'YouNow moment'
90
91     entry = {
92         'id': compat_str(item['momentId']),
93         'title': title,
94         'view_count': int_or_none(item.get('views')),
95         'like_count': int_or_none(item.get('likes')),
96         'timestamp': int_or_none(item.get('created')),
97         'formats': [{
98             'url': STREAM_URL_FORMAT % (item['momentId'], item['momentId']),
99             'ext': 'mp4',
100             'protocol': 'm3u8',
101         }],
102     }
103
104     try:
105         entry['uploader'] = entry['creator'] = item['owner']['name']
106         entry['uploader_url'] = 'https://www.younow.com/%s' % (item['owner']['name'],)
107         entry['uploader_id'] = item['owner']['userId']
108     except KeyError:
109         pass
110
111     return entry
112
113
114 class YouNowChannelIE(InfoExtractor):
115     _VALID_URL = r'https?://(?:www\.)?younow\.com/(?P<id>[^/]+)/channel'
116     _TEST = {
117         'url': 'https://www.younow.com/Kate_Swiz/channel',
118         'info_dict': {
119             'title': 'Kate_Swiz moments'
120         },
121         'playlist_count': 6,
122     }
123
124     MOMENTS_URL_FORMAT = 'https://cdn.younow.com/php/api/moment/profile/channelId=%s/createdBefore=%d/records=20'
125
126     def _real_extract(self, url):
127         entries = []
128         username = self._match_id(url)
129         user_info = self._download_json('https://api.younow.com/php/api/broadcast/info/curId=0/user=%s' % (username), username, note='Downloading user information')
130         channel_id = user_info['userId']
131         created_before = 0
132         moment_ids = []
133         moment_ids_processed = []
134         err = False
135
136         while True:
137             if created_before:
138                 cb = datetime.fromtimestamp(created_before)
139             else:
140                 cb = datetime.now()
141             info = self._download_json(self.MOMENTS_URL_FORMAT % (channel_id, created_before), username, note='Downloading moments data (created before %s)' % (cb))
142
143             for item in info['items']:
144                 if item['type'] == 'moment':
145                     entry = _moment_to_entry(item)
146                     moment_ids_processed.append(entry['id'])
147                     entries.append(entry)
148                 elif item['type'] == 'collection':
149                     moment_ids += [compat_str(x) for x in item['momentsIds']]
150
151                 try:
152                     created_before = int_or_none(item['created'])
153                 except KeyError:
154                     err = True
155                     break
156
157             if (err or
158                     not info['hasMore'] or
159                     'items' not in info or
160                     not info['items']):
161                 break
162
163         for mid in set(moment_ids):
164             if mid in moment_ids_processed:
165                 continue
166             item = self._download_json(MOMENT_URL_FORMAT % (mid), mid)
167             entries.append(_moment_to_entry(item['item']))
168
169         return self.playlist_result(entries, playlist_title='%s moments' % (username))
170
171
172 class YouNowMomentIE(InfoExtractor):
173     _VALID_URL = r'https?://(?:www\.)?younow\.com/[^/]+/(?P<id>[^/]+)/[^/]+'
174     _TEST = {
175         'url': 'https://www.younow.com/GABO.../20712117/36319236/3b316doc/m',
176         'info_dict': {
177             'id': '20712117',
178             'ext': 'mp4',
179             'title': 'YouNow capture',
180             'view_count': 19,
181             'like_count': 0,
182             'timestamp': 1490432040,
183             'formats': [{
184                 'url': 'https://hls.younow.com/momentsplaylists/live/20712117/20712117.m3u8',
185                 'ext': 'mp4',
186                 'protocol': 'm3u8',
187             }],
188             'upload_date': '20170325',
189             'uploader': 'GABO...',
190             'uploader_id': 35917228,
191         },
192     }
193
194     def _real_extract(self, url):
195         mid = self._match_id(url)
196         item = self._download_json(MOMENT_URL_FORMAT % (mid), mid)
197         return _moment_to_entry(item['item'])