]>
Commit | Line | Data |
---|---|---|
dcdb292f | 1 | # coding: utf-8 |
659aa21b | 2 | from __future__ import unicode_literals |
3 | ||
4 | import re | |
5 | ||
6 | from .common import InfoExtractor | |
884cdb6c S |
7 | from ..compat import ( |
8 | compat_str, | |
9 | compat_urlparse, | |
10 | ) | |
bcb891e8 | 11 | from ..utils import ( |
75427031 | 12 | determine_ext, |
e5437320 | 13 | ExtractorError, |
bcb891e8 | 14 | int_or_none, |
e5437320 | 15 | parse_iso8601, |
062a3fdf | 16 | remove_end, |
bcb891e8 | 17 | ) |
659aa21b | 18 | |
19 | ||
20 | class LifeNewsIE(InfoExtractor): | |
5181759c S |
21 | IE_NAME = 'life' |
22 | IE_DESC = 'Life.ru' | |
23 | _VALID_URL = r'https?://life\.ru/t/[^/]+/(?P<id>\d+)' | |
bcb891e8 | 24 | |
848edeab | 25 | _TESTS = [{ |
e7998f59 | 26 | # single video embedded via video/source |
5181759c | 27 | 'url': 'https://life.ru/t/новости/98736', |
e7998f59 | 28 | 'md5': '77c95eaefaca216e32a76a343ad89d23', |
659aa21b | 29 | 'info_dict': { |
e7998f59 | 30 | 'id': '98736', |
bcb891e8 | 31 | 'ext': 'mp4', |
e7998f59 S |
32 | 'title': 'Мужчина нашел дома архив оборонного завода', |
33 | 'description': 'md5:3b06b1b39b5e2bea548e403d99b8bf26', | |
e5437320 | 34 | 'timestamp': 1344154740, |
e7998f59 | 35 | 'upload_date': '20120805', |
e5437320 | 36 | 'view_count': int, |
659aa21b | 37 | } |
848edeab | 38 | }, { |
e7998f59 | 39 | # single video embedded via iframe |
5181759c | 40 | 'url': 'https://life.ru/t/новости/152125', |
848edeab YCH |
41 | 'md5': '77d19a6f0886cd76bdbf44b4d971a273', |
42 | 'info_dict': { | |
43 | 'id': '152125', | |
44 | 'ext': 'mp4', | |
45 | 'title': 'В Сети появилось видео захвата «Правым сектором» колхозных полей ', | |
46 | 'description': 'Жители двух поселков Днепропетровской области не простили радикалам угрозу лишения плодородных земель и пошли в лобовую. ', | |
e5437320 | 47 | 'timestamp': 1427961840, |
848edeab | 48 | 'upload_date': '20150402', |
e5437320 | 49 | 'view_count': int, |
848edeab | 50 | } |
07d2921c | 51 | }, { |
e7998f59 | 52 | # two videos embedded via iframe |
5181759c | 53 | 'url': 'https://life.ru/t/новости/153461', |
07d2921c YCH |
54 | 'info_dict': { |
55 | 'id': '153461', | |
07d2921c YCH |
56 | 'title': 'В Москве спасли потерявшегося медвежонка, который спрятался на дереве', |
57 | 'description': 'Маленький хищник не смог найти дорогу домой и обрел временное убежище на тополе недалеко от жилого массива, пока его не нашла соседская собака.', | |
e5437320 S |
58 | 'timestamp': 1430825520, |
59 | 'view_count': int, | |
e7998f59 S |
60 | }, |
61 | 'playlist': [{ | |
62 | 'md5': '9b6ef8bc0ffa25aebc8bdb40d89ab795', | |
63 | 'info_dict': { | |
64 | 'id': '153461-video1', | |
65 | 'ext': 'mp4', | |
66 | 'title': 'В Москве спасли потерявшегося медвежонка, который спрятался на дереве (Видео 1)', | |
67 | 'description': 'Маленький хищник не смог найти дорогу домой и обрел временное убежище на тополе недалеко от жилого массива, пока его не нашла соседская собака.', | |
e5437320 | 68 | 'timestamp': 1430825520, |
e7998f59 S |
69 | 'upload_date': '20150505', |
70 | }, | |
71 | }, { | |
72 | 'md5': 'ebb3bf3b1ce40e878d0d628e93eb0322', | |
73 | 'info_dict': { | |
74 | 'id': '153461-video2', | |
75 | 'ext': 'mp4', | |
76 | 'title': 'В Москве спасли потерявшегося медвежонка, который спрятался на дереве (Видео 2)', | |
77 | 'description': 'Маленький хищник не смог найти дорогу домой и обрел временное убежище на тополе недалеко от жилого массива, пока его не нашла соседская собака.', | |
e5437320 | 78 | 'timestamp': 1430825520, |
e7998f59 S |
79 | 'upload_date': '20150505', |
80 | }, | |
81 | }], | |
057ebeac | 82 | }, { |
5181759c S |
83 | 'url': 'https://life.ru/t/новости/213035', |
84 | 'only_matching': True, | |
85 | }, { | |
86 | 'url': 'https://life.ru/t/%D0%BD%D0%BE%D0%B2%D0%BE%D1%81%D1%82%D0%B8/153461', | |
87 | 'only_matching': True, | |
88 | }, { | |
89 | 'url': 'https://life.ru/t/новости/411489/manuel_vals_nazval_frantsiiu_tsieliu_nomier_odin_dlia_ighil', | |
057ebeac | 90 | 'only_matching': True, |
848edeab | 91 | }] |
659aa21b | 92 | |
93 | def _real_extract(self, url): | |
5181759c | 94 | video_id = self._match_id(url) |
659aa21b | 95 | |
5181759c | 96 | webpage = self._download_webpage(url, video_id) |
659aa21b | 97 | |
e7998f59 S |
98 | video_urls = re.findall( |
99 | r'<video[^>]+><source[^>]+src=["\'](.+?)["\']', webpage) | |
100 | ||
101 | iframe_links = re.findall( | |
884cdb6c | 102 | r'<iframe[^>]+src=["\']((?:https?:)?//embed\.life\.ru/(?:embed|video)/.+?)["\']', |
e7998f59 S |
103 | webpage) |
104 | ||
105 | if not video_urls and not iframe_links: | |
fc26f3b4 | 106 | raise ExtractorError('No media links available for %s' % video_id) |
659aa21b | 107 | |
062a3fdf S |
108 | title = remove_end( |
109 | self._og_search_title(webpage), | |
5181759c | 110 | ' - Life.ru') |
659aa21b | 111 | |
112 | description = self._og_search_description(webpage) | |
113 | ||
114 | view_count = self._html_search_regex( | |
e5437320 S |
115 | r'<div[^>]+class=(["\']).*?\bhits-count\b.*?\1[^>]*>\s*(?P<value>\d+)\s*</div>', |
116 | webpage, 'view count', fatal=False, group='value') | |
659aa21b | 117 | |
e5437320 S |
118 | timestamp = parse_iso8601(self._search_regex( |
119 | r'<time[^>]+datetime=(["\'])(?P<value>.+?)\1', | |
120 | webpage, 'upload date', fatal=False, group='value')) | |
659aa21b | 121 | |
848edeab YCH |
122 | common_info = { |
123 | 'description': description, | |
124 | 'view_count': int_or_none(view_count), | |
e5437320 | 125 | 'timestamp': timestamp, |
848edeab YCH |
126 | } |
127 | ||
e7998f59 | 128 | def make_entry(video_id, video_url, index=None): |
848edeab YCH |
129 | cur_info = dict(common_info) |
130 | cur_info.update({ | |
e7998f59 S |
131 | 'id': video_id if not index else '%s-video%s' % (video_id, index), |
132 | 'url': video_url, | |
133 | 'title': title if not index else '%s (Видео %s)' % (title, index), | |
848edeab YCH |
134 | }) |
135 | return cur_info | |
136 | ||
e7998f59 S |
137 | def make_video_entry(video_id, video_url, index=None): |
138 | video_url = compat_urlparse.urljoin(url, video_url) | |
139 | return make_entry(video_id, video_url, index) | |
140 | ||
141 | def make_iframe_entry(video_id, video_url, index=None): | |
142 | video_url = self._proto_relative_url(video_url, 'http:') | |
143 | cur_info = make_entry(video_id, video_url, index) | |
144 | cur_info['_type'] = 'url_transparent' | |
848edeab | 145 | return cur_info |
fc26f3b4 | 146 | |
e7998f59 S |
147 | if len(video_urls) == 1 and not iframe_links: |
148 | return make_video_entry(video_id, video_urls[0]) | |
149 | ||
150 | if len(iframe_links) == 1 and not video_urls: | |
151 | return make_iframe_entry(video_id, iframe_links[0]) | |
152 | ||
153 | entries = [] | |
154 | ||
155 | if video_urls: | |
156 | for num, video_url in enumerate(video_urls, 1): | |
157 | entries.append(make_video_entry(video_id, video_url, num)) | |
158 | ||
159 | if iframe_links: | |
160 | for num, iframe_link in enumerate(iframe_links, len(video_urls) + 1): | |
161 | entries.append(make_iframe_entry(video_id, iframe_link, num)) | |
162 | ||
163 | playlist = common_info.copy() | |
164 | playlist.update(self.playlist_result(entries, video_id, title, description)) | |
165 | return playlist | |
75427031 S |
166 | |
167 | ||
168 | class LifeEmbedIE(InfoExtractor): | |
169 | IE_NAME = 'life:embed' | |
884cdb6c | 170 | _VALID_URL = r'https?://embed\.life\.ru/(?:embed|video)/(?P<id>[\da-f]{32})' |
75427031 | 171 | |
884cdb6c | 172 | _TESTS = [{ |
75427031 S |
173 | 'url': 'http://embed.life.ru/embed/e50c2dec2867350528e2574c899b8291', |
174 | 'md5': 'b889715c9e49cb1981281d0e5458fbbe', | |
175 | 'info_dict': { | |
176 | 'id': 'e50c2dec2867350528e2574c899b8291', | |
177 | 'ext': 'mp4', | |
178 | 'title': 'e50c2dec2867350528e2574c899b8291', | |
ec85ded8 | 179 | 'thumbnail': r're:http://.*\.jpg', |
75427031 | 180 | } |
884cdb6c S |
181 | }, { |
182 | # with 1080p | |
183 | 'url': 'https://embed.life.ru/video/e50c2dec2867350528e2574c899b8291', | |
184 | 'only_matching': True, | |
185 | }] | |
75427031 S |
186 | |
187 | def _real_extract(self, url): | |
188 | video_id = self._match_id(url) | |
189 | ||
190 | webpage = self._download_webpage(url, video_id) | |
191 | ||
884cdb6c | 192 | thumbnail = None |
75427031 | 193 | formats = [] |
884cdb6c S |
194 | |
195 | def extract_m3u8(manifest_url): | |
196 | formats.extend(self._extract_m3u8_formats( | |
197 | manifest_url, video_id, 'mp4', | |
198 | entry_protocol='m3u8_native', m3u8_id='m3u8')) | |
199 | ||
200 | def extract_original(original_url): | |
201 | formats.append({ | |
202 | 'url': original_url, | |
203 | 'format_id': determine_ext(original_url, None), | |
204 | 'preference': 1, | |
205 | }) | |
206 | ||
207 | playlist = self._parse_json( | |
208 | self._search_regex( | |
209 | r'options\s*=\s*({.+?});', webpage, 'options', default='{}'), | |
210 | video_id).get('playlist', {}) | |
211 | if playlist: | |
212 | master = playlist.get('master') | |
213 | if isinstance(master, compat_str) and determine_ext(master) == 'm3u8': | |
214 | extract_m3u8(compat_urlparse.urljoin(url, master)) | |
215 | original = playlist.get('original') | |
216 | if isinstance(original, compat_str): | |
217 | extract_original(original) | |
218 | thumbnail = playlist.get('image') | |
219 | ||
220 | # Old rendition fallback | |
221 | if not formats: | |
222 | for video_url in re.findall(r'"file"\s*:\s*"([^"]+)', webpage): | |
223 | video_url = compat_urlparse.urljoin(url, video_url) | |
224 | if determine_ext(video_url) == 'm3u8': | |
225 | extract_m3u8(video_url) | |
226 | else: | |
227 | extract_original(video_url) | |
228 | ||
95eb1add | 229 | self._sort_formats(formats) |
75427031 | 230 | |
884cdb6c | 231 | thumbnail = thumbnail or self._search_regex( |
75427031 S |
232 | r'"image"\s*:\s*"([^"]+)', webpage, 'thumbnail', default=None) |
233 | ||
234 | return { | |
235 | 'id': video_id, | |
236 | 'title': video_id, | |
237 | 'thumbnail': thumbnail, | |
238 | 'formats': formats, | |
239 | } |