[twitter:card] Use _html_search_regex
[youtube-dl] / youtube_dl / extractor / twitter.py
1 # coding: utf-8
2 from __future__ import unicode_literals
3
4 import re
5
6 from .common import InfoExtractor
7 from ..compat import compat_urllib_request
8 from ..utils import (
9     float_or_none,
10     xpath_text,
11     remove_end,
12 )
13
14
15 class TwitterCardIE(InfoExtractor):
16     IE_NAME = 'twitter:card'
17     _VALID_URL = r'https?://(?:www\.)?twitter\.com/i/cards/tfw/v1/(?P<id>\d+)'
18     _TESTS = [
19         {
20             'url': 'https://twitter.com/i/cards/tfw/v1/560070183650213889',
21             'md5': '7d2f6b4d2eb841a7ccc893d479bfceb4',
22             'info_dict': {
23                 'id': '560070183650213889',
24                 'ext': 'mp4',
25                 'title': 'TwitterCard',
26                 'thumbnail': 're:^https?://.*\.jpg$',
27                 'duration': 30.033,
28             }
29         },
30         {
31             'url': 'https://twitter.com/i/cards/tfw/v1/623160978427936768',
32             'md5': '7ee2a553b63d1bccba97fbed97d9e1c8',
33             'info_dict': {
34                 'id': '623160978427936768',
35                 'ext': 'mp4',
36                 'title': 'TwitterCard',
37                 'thumbnail': 're:^https?://.*\.jpg',
38                 'duration': 80.155,
39             },
40         }
41     ]
42
43     def _real_extract(self, url):
44         video_id = self._match_id(url)
45
46         # Different formats served for different User-Agents
47         USER_AGENTS = [
48             'Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20150101 Firefox/20.0 (Chrome)',  # mp4
49             'Mozilla/5.0 (Windows NT 5.2; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0',  # webm
50         ]
51
52         config = None
53         formats = []
54         for user_agent in USER_AGENTS:
55             request = compat_urllib_request.Request(url)
56             request.add_header('User-Agent', user_agent)
57             webpage = self._download_webpage(request, video_id)
58
59             config = self._parse_json(self._html_search_regex(
60                 r'data-player-config="([^"]+)"', webpage, 'data player config'),
61                 video_id)
62             if 'playlist' not in config:
63                 if 'vmapUrl' in config:
64                     vmap_data = self._download_xml(config['vmapUrl'], video_id)
65                     video_url = xpath_text(vmap_data, './/MediaFile').strip()
66                     formats.append({
67                         'url': video_url,
68                     })
69                     break   # same video regardless of UA
70                 continue
71
72             video_url = config['playlist'][0]['source']
73
74             f = {
75                 'url': video_url,
76             }
77
78             m = re.search(r'/(?P<width>\d+)x(?P<height>\d+)/', video_url)
79             if m:
80                 f.update({
81                     'width': int(m.group('width')),
82                     'height': int(m.group('height')),
83                 })
84             formats.append(f)
85         self._sort_formats(formats)
86
87         thumbnail = config.get('posterImageUrl')
88         duration = float_or_none(config.get('duration'))
89
90         return {
91             'id': video_id,
92             'title': 'TwitterCard',
93             'thumbnail': thumbnail,
94             'duration': duration,
95             'formats': formats,
96         }
97
98
99 class TwitterIE(InfoExtractor):
100     IE_NAME = 'twitter'
101     _VALID_URL = r'https?://(?:www\.|m\.|mobile\.)?twitter\.com/(?P<user_id>[^/]+)/status/(?P<id>\d+)'
102     _TEMPLATE_URL = 'https://twitter.com/%s/status/%s'
103
104     _TEST = {
105         'url': 'https://twitter.com/freethenipple/status/643211948184596480',
106         'md5': '31cd83a116fc41f99ae3d909d4caf6a0',
107         'info_dict': {
108             'id': '643211948184596480',
109             'ext': 'mp4',
110             'title': 'FREE THE NIPPLE - FTN supporters on Hollywood Blvd today!',
111             'thumbnail': 're:^https?://.*\.jpg',
112             'duration': 12.922,
113             'description': 'FREE THE NIPPLE on Twitter: "FTN supporters on Hollywood Blvd today! http://t.co/c7jHH749xJ"',
114             'uploader': 'FREE THE NIPPLE',
115             'uploader_id': 'freethenipple',
116         },
117     }
118
119     def _real_extract(self, url):
120         mobj = re.match(self._VALID_URL, url)
121         user_id = mobj.group('user_id')
122         twid = mobj.group('id')
123
124         webpage = self._download_webpage(self._TEMPLATE_URL % (user_id, twid), twid)
125
126         username = remove_end(self._og_search_title(webpage), ' on Twitter')
127
128         title = self._og_search_description(webpage).strip('').replace('\n', ' ')
129
130         # strip  'https -_t.co_BJYgOjSeGA' junk from filenames
131         mobj = re.match(r'“(.*)\s+(http://[^ ]+)”', title)
132         title, short_url = mobj.groups()
133
134         card_id = self._search_regex(
135             r'["\']/i/cards/tfw/v1/(\d+)', webpage, 'twitter card url')
136         card_url = 'https://twitter.com/i/cards/tfw/v1/' + card_id
137
138         return {
139             '_type': 'url_transparent',
140             'ie_key': 'TwitterCard',
141             'uploader_id': user_id,
142             'uploader': username,
143             'url': card_url,
144             'webpage_url': url,
145             'description': '%s on Twitter: "%s %s"' % (username, title, short_url),
146             'title': username + ' - ' + title,
147         }