]> jfr.im git - yt-dlp.git/blob - yt_dlp/extractor/younow.py
[ie/orf:on] Improve extraction (#9677)
[yt-dlp.git] / yt_dlp / extractor / younow.py
1 import itertools
2
3 from .common import InfoExtractor
4 from ..compat import compat_str
5 from ..utils import (
6 ExtractorError,
7 format_field,
8 int_or_none,
9 str_or_none,
10 try_get,
11 )
12
13 CDN_API_BASE = 'https://cdn.younow.com/php/api'
14 MOMENT_URL_FORMAT = '%s/moment/fetch/id=%%s' % CDN_API_BASE
15
16
17 class YouNowLiveIE(InfoExtractor):
18 _VALID_URL = r'https?://(?:www\.)?younow\.com/(?P<id>[^/?#&]+)'
19 _TEST = {
20 'url': 'https://www.younow.com/AmandaPadeezy',
21 'info_dict': {
22 'id': 'AmandaPadeezy',
23 'ext': 'mp4',
24 'is_live': True,
25 'title': 'March 26, 2017',
26 'thumbnail': r're:^https?://.*\.jpg$',
27 'tags': ['girls'],
28 'categories': ['girls'],
29 'uploader': 'AmandaPadeezy',
30 'uploader_id': '6716501',
31 'uploader_url': 'https://www.younow.com/AmandaPadeezy',
32 'creator': 'AmandaPadeezy',
33 },
34 'skip': True,
35 }
36
37 @classmethod
38 def suitable(cls, url):
39 return (False
40 if YouNowChannelIE.suitable(url) or YouNowMomentIE.suitable(url)
41 else super(YouNowLiveIE, cls).suitable(url))
42
43 def _real_extract(self, url):
44 username = self._match_id(url)
45
46 data = self._download_json(
47 'https://api.younow.com/php/api/broadcast/info/curId=0/user=%s'
48 % username, username)
49
50 if data.get('errorCode') != 0:
51 raise ExtractorError(data['errorMsg'], expected=True)
52
53 uploader = try_get(
54 data, lambda x: x['user']['profileUrlString'],
55 compat_str) or username
56
57 return {
58 'id': uploader,
59 'is_live': True,
60 'title': uploader,
61 'thumbnail': data.get('awsUrl'),
62 'tags': data.get('tags'),
63 'categories': data.get('tags'),
64 'uploader': uploader,
65 'uploader_id': data.get('userId'),
66 'uploader_url': 'https://www.younow.com/%s' % username,
67 'creator': uploader,
68 'view_count': int_or_none(data.get('viewers')),
69 'like_count': int_or_none(data.get('likes')),
70 'formats': [{
71 'url': '%s/broadcast/videoPath/hls=1/broadcastId=%s/channelId=%s'
72 % (CDN_API_BASE, data['broadcastId'], data['userId']),
73 'ext': 'mp4',
74 'protocol': 'm3u8',
75 }],
76 }
77
78
79 def _extract_moment(item, fatal=True):
80 moment_id = item.get('momentId')
81 if not moment_id:
82 if not fatal:
83 return
84 raise ExtractorError('Unable to extract moment id')
85
86 moment_id = compat_str(moment_id)
87
88 title = item.get('text')
89 if not title:
90 title = 'YouNow %s' % (
91 item.get('momentType') or item.get('titleType') or 'moment')
92
93 uploader = try_get(item, lambda x: x['owner']['name'], compat_str)
94 uploader_id = try_get(item, lambda x: x['owner']['userId'])
95 uploader_url = format_field(uploader, None, 'https://www.younow.com/%s')
96
97 entry = {
98 'extractor_key': 'YouNowMoment',
99 'id': moment_id,
100 'title': title,
101 'view_count': int_or_none(item.get('views')),
102 'like_count': int_or_none(item.get('likes')),
103 'timestamp': int_or_none(item.get('created')),
104 'creator': uploader,
105 'uploader': uploader,
106 'uploader_id': str_or_none(uploader_id),
107 'uploader_url': uploader_url,
108 'formats': [{
109 'url': 'https://hls.younow.com/momentsplaylists/live/%s/%s.m3u8'
110 % (moment_id, moment_id),
111 'ext': 'mp4',
112 'protocol': 'm3u8_native',
113 }],
114 }
115
116 return entry
117
118
119 class YouNowChannelIE(InfoExtractor):
120 _VALID_URL = r'https?://(?:www\.)?younow\.com/(?P<id>[^/]+)/channel'
121 _TEST = {
122 'url': 'https://www.younow.com/its_Kateee_/channel',
123 'info_dict': {
124 'id': '14629760',
125 'title': 'its_Kateee_ moments'
126 },
127 'playlist_mincount': 8,
128 }
129
130 def _entries(self, username, channel_id):
131 created_before = 0
132 for page_num in itertools.count(1):
133 if created_before is None:
134 break
135 info = self._download_json(
136 '%s/moment/profile/channelId=%s/createdBefore=%d/records=20'
137 % (CDN_API_BASE, channel_id, created_before), username,
138 note='Downloading moments page %d' % page_num)
139 items = info.get('items')
140 if not items or not isinstance(items, list):
141 break
142 for item in items:
143 if not isinstance(item, dict):
144 continue
145 item_type = item.get('type')
146 if item_type == 'moment':
147 entry = _extract_moment(item, fatal=False)
148 if entry:
149 yield entry
150 elif item_type == 'collection':
151 moments = item.get('momentsIds')
152 if isinstance(moments, list):
153 for moment_id in moments:
154 m = self._download_json(
155 MOMENT_URL_FORMAT % moment_id, username,
156 note='Downloading %s moment JSON' % moment_id,
157 fatal=False)
158 if m and isinstance(m, dict) and m.get('item'):
159 entry = _extract_moment(m['item'])
160 if entry:
161 yield entry
162 created_before = int_or_none(item.get('created'))
163
164 def _real_extract(self, url):
165 username = self._match_id(url)
166 channel_id = compat_str(self._download_json(
167 'https://api.younow.com/php/api/broadcast/info/curId=0/user=%s'
168 % username, username, note='Downloading user information')['userId'])
169 return self.playlist_result(
170 self._entries(username, channel_id), channel_id,
171 '%s moments' % username)
172
173
174 class YouNowMomentIE(InfoExtractor):
175 _VALID_URL = r'https?://(?:www\.)?younow\.com/[^/]+/(?P<id>[^/?#&]+)'
176 _TEST = {
177 'url': 'https://www.younow.com/GABO.../20712117/36319236/3b316doc/m',
178 'md5': 'a30c70eadb9fb39a1aa3c8c0d22a0807',
179 'info_dict': {
180 'id': '20712117',
181 'ext': 'mp4',
182 'title': 'YouNow capture',
183 'view_count': int,
184 'like_count': int,
185 'timestamp': 1490432040,
186 'upload_date': '20170325',
187 'uploader': 'GABO...',
188 'uploader_id': '35917228',
189 },
190 }
191
192 @classmethod
193 def suitable(cls, url):
194 return (False
195 if YouNowChannelIE.suitable(url)
196 else super(YouNowMomentIE, cls).suitable(url))
197
198 def _real_extract(self, url):
199 video_id = self._match_id(url)
200 item = self._download_json(MOMENT_URL_FORMAT % video_id, video_id)
201 return _extract_moment(item['item'])