]>
Commit | Line | Data |
---|---|---|
1fd6e309 RA |
1 | # -*- coding: utf-8 -*- |
2 | from __future__ import unicode_literals | |
3 | ||
4 | import re | |
5 | import time | |
6 | import xml.etree.ElementTree as etree | |
7 | ||
8 | from .common import InfoExtractor | |
7a730921 | 9 | from ..compat import compat_urlparse |
1fd6e309 RA |
10 | from ..utils import ( |
11 | unescapeHTML, | |
12 | urlencode_postdata, | |
13 | unified_timestamp, | |
7a730921 | 14 | ExtractorError, |
1fd6e309 RA |
15 | ) |
16 | ||
17 | ||
1b6712ab RA |
18 | MSO_INFO = { |
19 | 'DTV': { | |
20 | 'name': 'DirecTV', | |
21 | 'username_field': 'username', | |
22 | 'password_field': 'password', | |
23 | }, | |
24 | 'Rogers': { | |
25 | 'name': 'Rogers Cable', | |
26 | 'username_field': 'UserName', | |
27 | 'password_field': 'UserPassword', | |
28 | }, | |
29 | } | |
30 | ||
31 | ||
818ac213 | 32 | class AdobePassIE(InfoExtractor): |
1fd6e309 RA |
33 | _SERVICE_PROVIDER_TEMPLATE = 'https://sp.auth.adobe.com/adobe-services/%s' |
34 | _USER_AGENT = 'Mozilla/5.0 (X11; Linux i686; rv:47.0) Gecko/20100101 Firefox/47.0' | |
1dec2c8a | 35 | _MVPD_CACHE = 'ap-mvpd' |
1fd6e309 RA |
36 | |
37 | @staticmethod | |
38 | def _get_mvpd_resource(provider_id, title, guid, rating): | |
39 | channel = etree.Element('channel') | |
40 | channel_title = etree.SubElement(channel, 'title') | |
41 | channel_title.text = provider_id | |
42 | item = etree.SubElement(channel, 'item') | |
43 | resource_title = etree.SubElement(item, 'title') | |
44 | resource_title.text = title | |
45 | resource_guid = etree.SubElement(item, 'guid') | |
46 | resource_guid.text = guid | |
47 | resource_rating = etree.SubElement(item, 'media:rating') | |
48 | resource_rating.attrib = {'scheme': 'urn:v-chip'} | |
49 | resource_rating.text = rating | |
50 | return '<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">' + etree.tostring(channel).decode() + '</rss>' | |
51 | ||
52 | def _extract_mvpd_auth(self, url, video_id, requestor_id, resource): | |
53 | def xml_text(xml_str, tag): | |
54 | return self._search_regex( | |
55 | '<%s>(.+?)</%s>' % (tag, tag), xml_str, tag) | |
56 | ||
6150502e RA |
57 | def is_expired(token, date_ele): |
58 | token_expires = unified_timestamp(re.sub(r'[_ ]GMT', '', xml_text(token, date_ele))) | |
59 | return token_expires and token_expires <= int(time.time()) | |
60 | ||
1b6712ab RA |
61 | def post_form(form_page_res, note, data={}): |
62 | form_page, urlh = form_page_res | |
63 | post_url = self._html_search_regex(r'<form[^>]+action=(["\'])(?P<url>.+?)\1', form_page, 'post url', group='url') | |
64 | if not re.match(r'https?://', post_url): | |
65 | post_url = compat_urlparse.urljoin(urlh.geturl(), post_url) | |
66 | form_data = self._hidden_inputs(form_page) | |
67 | form_data.update(data) | |
68 | return self._download_webpage_handle( | |
69 | post_url, video_id, note, data=urlencode_postdata(form_data), headers={ | |
70 | 'Content-Type': 'application/x-www-form-urlencoded', | |
71 | }) | |
72 | ||
7a730921 | 73 | def raise_mvpd_required(): |
8414c2da S |
74 | raise ExtractorError( |
75 | 'This video is only available for users of participating TV providers. ' | |
797c636b RA |
76 | 'Use --ap-mso to specify Adobe Pass Multiple-system operator Identifier ' |
77 | 'and --ap-username and --ap-password or --netrc to provide account credentials.', expected=True) | |
7a730921 | 78 | |
1fd6e309 RA |
79 | mvpd_headers = { |
80 | 'ap_42': 'anonymous', | |
81 | 'ap_11': 'Linux i686', | |
82 | 'ap_z': self._USER_AGENT, | |
83 | 'User-Agent': self._USER_AGENT, | |
84 | } | |
85 | ||
86 | guid = xml_text(resource, 'guid') | |
1b6712ab | 87 | count = 0 |
5712c0f4 | 88 | while count < 2: |
1dec2c8a | 89 | requestor_info = self._downloader.cache.load(self._MVPD_CACHE, requestor_id) or {} |
1b6712ab RA |
90 | authn_token = requestor_info.get('authn_token') |
91 | if authn_token and is_expired(authn_token, 'simpleTokenExpires'): | |
92 | authn_token = None | |
93 | if not authn_token: | |
94 | # TODO add support for other TV Providers | |
797c636b | 95 | mso_id = self._downloader.params.get('ap_mso') |
1b6712ab RA |
96 | if not mso_id: |
97 | raise_mvpd_required() | |
1b6712ab RA |
98 | username, password = self._get_login_info('ap_username', 'ap_password', mso_id) |
99 | if not username or not password: | |
100 | raise_mvpd_required() | |
101 | mso_info = MSO_INFO[mso_id] | |
102 | ||
103 | provider_redirect_page_res = self._download_webpage_handle( | |
104 | self._SERVICE_PROVIDER_TEMPLATE % 'authenticate/saml', video_id, | |
105 | 'Downloading Provider Redirect Page', query={ | |
106 | 'noflash': 'true', | |
107 | 'mso_id': mso_id, | |
108 | 'requestor_id': requestor_id, | |
109 | 'no_iframe': 'false', | |
110 | 'domain_name': 'adobe.com', | |
111 | 'redirect_url': url, | |
1fd6e309 | 112 | }) |
1b6712ab RA |
113 | provider_login_page_res = post_form( |
114 | provider_redirect_page_res, 'Downloading Provider Login Page') | |
115 | mvpd_confirm_page_res = post_form(provider_login_page_res, 'Logging in', { | |
116 | mso_info['username_field']: username, | |
117 | mso_info['password_field']: password, | |
1fd6e309 | 118 | }) |
1b6712ab RA |
119 | if mso_id == 'DTV': |
120 | post_form(mvpd_confirm_page_res, 'Confirming Login') | |
121 | ||
122 | session = self._download_webpage( | |
123 | self._SERVICE_PROVIDER_TEMPLATE % 'session', video_id, | |
124 | 'Retrieving Session', data=urlencode_postdata({ | |
125 | '_method': 'GET', | |
126 | 'requestor_id': requestor_id, | |
127 | }), headers=mvpd_headers) | |
128 | if '<pendingLogout' in session: | |
1dec2c8a | 129 | self._downloader.cache.store(self._MVPD_CACHE, requestor_id, {}) |
1b6712ab RA |
130 | count += 1 |
131 | continue | |
132 | authn_token = unescapeHTML(xml_text(session, 'authnToken')) | |
133 | requestor_info['authn_token'] = authn_token | |
1dec2c8a | 134 | self._downloader.cache.store(self._MVPD_CACHE, requestor_id, requestor_info) |
1b6712ab RA |
135 | |
136 | authz_token = requestor_info.get(guid) | |
137 | if authz_token and is_expired(authz_token, 'simpleTokenTTL'): | |
138 | authz_token = None | |
139 | if not authz_token: | |
140 | authorize = self._download_webpage( | |
141 | self._SERVICE_PROVIDER_TEMPLATE % 'authorize', video_id, | |
142 | 'Retrieving Authorization Token', data=urlencode_postdata({ | |
143 | 'resource_id': resource, | |
144 | 'requestor_id': requestor_id, | |
145 | 'authentication_token': authn_token, | |
146 | 'mso_id': xml_text(authn_token, 'simpleTokenMsoID'), | |
147 | 'userMeta': '1', | |
148 | }), headers=mvpd_headers) | |
149 | if '<pendingLogout' in authorize: | |
1dec2c8a | 150 | self._downloader.cache.store(self._MVPD_CACHE, requestor_id, {}) |
1b6712ab RA |
151 | count += 1 |
152 | continue | |
153 | authz_token = unescapeHTML(xml_text(authorize, 'authzToken')) | |
154 | requestor_info[guid] = authz_token | |
1dec2c8a | 155 | self._downloader.cache.store(self._MVPD_CACHE, requestor_id, requestor_info) |
1b6712ab RA |
156 | |
157 | mvpd_headers.update({ | |
158 | 'ap_19': xml_text(authn_token, 'simpleSamlNameID'), | |
159 | 'ap_23': xml_text(authn_token, 'simpleSamlSessionIndex'), | |
160 | }) | |
161 | ||
162 | short_authorize = self._download_webpage( | |
163 | self._SERVICE_PROVIDER_TEMPLATE % 'shortAuthorize', | |
164 | video_id, 'Retrieving Media Token', data=urlencode_postdata({ | |
165 | 'authz_token': authz_token, | |
1fd6e309 | 166 | 'requestor_id': requestor_id, |
1b6712ab RA |
167 | 'session_guid': xml_text(authn_token, 'simpleTokenAuthenticationGuid'), |
168 | 'hashed_guid': 'false', | |
1fd6e309 | 169 | }), headers=mvpd_headers) |
1b6712ab | 170 | if '<pendingLogout' in short_authorize: |
1dec2c8a | 171 | self._downloader.cache.store(self._MVPD_CACHE, requestor_id, {}) |
1b6712ab RA |
172 | count += 1 |
173 | continue | |
174 | return short_authorize |