diff options
| -rwxr-xr-x | youtube_dl/YoutubeDL.py | 4 | ||||
| -rw-r--r-- | youtube_dl/__init__.py | 15 | ||||
| -rw-r--r-- | youtube_dl/extractor/adobepass.py | 210 | ||||
| -rw-r--r-- | youtube_dl/extractor/common.py | 10 | ||||
| -rw-r--r-- | youtube_dl/options.py | 24 | 
5 files changed, 157 insertions, 106 deletions
| diff --git a/youtube_dl/YoutubeDL.py b/youtube_dl/YoutubeDL.py index f70d5f49a..9c2c26280 100755 --- a/youtube_dl/YoutubeDL.py +++ b/youtube_dl/YoutubeDL.py @@ -131,7 +131,9 @@ class YoutubeDL(object):      username:          Username for authentication purposes.      password:          Password for authentication purposes.      videopassword:     Password for accessing a video. -    ap_mso_id          Adobe Pass Multiple-system operator Identifier. +    ap_mso_id:         Adobe Pass Multiple-system operator Identifier. +    ap_username:       TV Provider username for authentication purposes. +    ap_password:       TV Provider password for authentication purposes.      usenetrc:          Use netrc for authentication instead.      verbose:           Print additional info to stdout.      quiet:             Do not print messages to stdout. diff --git a/youtube_dl/__init__.py b/youtube_dl/__init__.py index 2b1b841c9..052f20ee7 100644 --- a/youtube_dl/__init__.py +++ b/youtube_dl/__init__.py @@ -34,12 +34,14 @@ from .utils import (      setproctitle,      std_headers,      write_string, +    render_table,  )  from .update import update_self  from .downloader import (      FileDownloader,  )  from .extractor import gen_extractors, list_extractors +from .extractor.adobepass import MSO_INFO  from .YoutubeDL import YoutubeDL @@ -118,18 +120,26 @@ def _real_main(argv=None):                  desc += ' (Example: "%s%s:%s" )' % (ie.SEARCH_KEY, random.choice(_COUNTS), random.choice(_SEARCHES))              write_string(desc + '\n', out=sys.stdout)          sys.exit(0) +    if opts.list_ap_mso_ids: +        table = [[mso_id, mso_info['name']] for mso_id, mso_info in MSO_INFO.items()] +        write_string('Supported TV Providers:\n' + render_table(['mso id', 'mso name'], table) + '\n', out=sys.stdout) +        sys.exit(0)      # Conflicting, missing and erroneous options      if opts.usenetrc and (opts.username is not None or opts.password is not None):          parser.error('using .netrc conflicts with giving username/password')      if opts.password is not None and opts.username is None:          parser.error('account username missing\n') +    if opts.ap_password is not None and opts.ap_username is None: +        parser.error('TV Provider account username missing\n')      if opts.outtmpl is not None and (opts.usetitle or opts.autonumber or opts.useid):          parser.error('using output template conflicts with using title, video ID or auto number')      if opts.usetitle and opts.useid:          parser.error('using title conflicts with using video ID')      if opts.username is not None and opts.password is None:          opts.password = compat_getpass('Type account password and press [Return]: ') +    if opts.ap_username is not None and opts.ap_password is None: +        opts.ap_password = compat_getpass('Type TV provider account password and press [Return]: ')      if opts.ratelimit is not None:          numeric_limit = FileDownloader.parse_bytes(opts.ratelimit)          if numeric_limit is None: @@ -169,6 +179,8 @@ def _real_main(argv=None):          opts.retries = parse_retries(opts.retries)      if opts.fragment_retries is not None:          opts.fragment_retries = parse_retries(opts.fragment_retries) +    if opts.ap_retries is not None: +        opts.ap_retries = parse_retries(opts.ap_retries)      if opts.buffersize is not None:          numeric_buffersize = FileDownloader.parse_bytes(opts.buffersize)          if numeric_buffersize is None: @@ -294,6 +306,9 @@ def _real_main(argv=None):          'twofactor': opts.twofactor,          'videopassword': opts.videopassword,          'ap_mso_id': opts.ap_mso_id, +        'ap_username': opts.ap_username, +        'ap_password': opts.ap_password, +        'ap_retries': opts.ap_retries,          'quiet': (opts.quiet or any_getting or any_printing),          'no_warnings': opts.no_warnings,          'forceurl': opts.geturl, diff --git a/youtube_dl/extractor/adobepass.py b/youtube_dl/extractor/adobepass.py index 50a208085..9add6c0f8 100644 --- a/youtube_dl/extractor/adobepass.py +++ b/youtube_dl/extractor/adobepass.py @@ -15,6 +15,20 @@ from ..utils import (  ) +MSO_INFO = { +    'DTV': { +        'name': 'DirecTV', +        'username_field': 'username', +        'password_field': 'password', +    }, +    'Rogers': { +        'name': 'Rogers Cable', +        'username_field': 'UserName', +        'password_field': 'UserPassword', +    }, +} + +  class AdobePassIE(InfoExtractor):      _SERVICE_PROVIDER_TEMPLATE = 'https://sp.auth.adobe.com/adobe-services/%s'      _USER_AGENT = 'Mozilla/5.0 (X11; Linux i686; rv:47.0) Gecko/20100101 Firefox/47.0' @@ -43,6 +57,18 @@ class AdobePassIE(InfoExtractor):              token_expires = unified_timestamp(re.sub(r'[_ ]GMT', '', xml_text(token, date_ele)))              return token_expires and token_expires <= int(time.time()) +        def post_form(form_page_res, note, data={}): +            form_page, urlh = form_page_res +            post_url = self._html_search_regex(r'<form[^>]+action=(["\'])(?P<url>.+?)\1', form_page, 'post url', group='url') +            if not re.match(r'https?://', post_url): +                post_url = compat_urlparse.urljoin(urlh.geturl(), post_url) +            form_data = self._hidden_inputs(form_page) +            form_data.update(data) +            return self._download_webpage_handle( +                post_url, video_id, note, data=urlencode_postdata(form_data), headers={ +                    'Content-Type': 'application/x-www-form-urlencoded', +                }) +          def raise_mvpd_required():              raise ExtractorError(                  'This video is only available for users of participating TV providers. ' @@ -57,105 +83,95 @@ class AdobePassIE(InfoExtractor):          }          guid = xml_text(resource, 'guid') -        requestor_info = self._downloader.cache.load('mvpd', requestor_id) or {} -        authn_token = requestor_info.get('authn_token') -        if authn_token and is_expired(authn_token, 'simpleTokenExpires'): -            authn_token = None -        if not authn_token: -            # TODO add support for other TV Providers -            mso_id = self._downloader.params.get('ap_mso_id') -            if not mso_id: -                raise_mvpd_required() -            username, password = self._get_netrc_login_info(mso_id) -            if not username or not password: -                return raise_mvpd_required() - -            def post_form(form_page_res, note, data={}): -                form_page, urlh = form_page_res -                post_url = self._html_search_regex(r'<form[^>]+action=(["\'])(?P<url>.+?)\1', form_page, 'post url', group='url') -                if not re.match(r'https?://', post_url): -                    post_url = compat_urlparse.urljoin(urlh.geturl(), post_url) -                form_data = self._hidden_inputs(form_page) -                form_data.update(data) -                return self._download_webpage_handle( -                    post_url, video_id, note, data=urlencode_postdata(form_data), headers={ -                        'Content-Type': 'application/x-www-form-urlencoded', +        retries = self._downloader.params.get('ap_retries', 3) +        count = 0 +        while count < retries: +            requestor_info = self._downloader.cache.load('mvpd', requestor_id) or {} +            authn_token = requestor_info.get('authn_token') +            if authn_token and is_expired(authn_token, 'simpleTokenExpires'): +                authn_token = None +            if not authn_token: +                # TODO add support for other TV Providers +                mso_id = self._downloader.params.get('ap_mso_id') +                if not mso_id: +                    raise_mvpd_required() +                if mso_id not in MSO_INFO: +                    raise ExtractorError( +                        'Unsupported TV Provider, use --list-ap-mso-ids to get a list of supported TV Providers' % mso_id, expected=True) +                username, password = self._get_login_info('ap_username', 'ap_password', mso_id) +                if not username or not password: +                    raise_mvpd_required() +                mso_info = MSO_INFO[mso_id] + +                provider_redirect_page_res = self._download_webpage_handle( +                    self._SERVICE_PROVIDER_TEMPLATE % 'authenticate/saml', video_id, +                    'Downloading Provider Redirect Page', query={ +                        'noflash': 'true', +                        'mso_id': mso_id, +                        'requestor_id': requestor_id, +                        'no_iframe': 'false', +                        'domain_name': 'adobe.com', +                        'redirect_url': url,                      }) - -            provider_redirect_page_res = self._download_webpage_handle( -                self._SERVICE_PROVIDER_TEMPLATE % 'authenticate/saml', video_id, -                'Downloading Provider Redirect Page', query={ -                    'noflash': 'true', -                    'mso_id': mso_id, -                    'requestor_id': requestor_id, -                    'no_iframe': 'false', -                    'domain_name': 'adobe.com', -                    'redirect_url': url, +                provider_login_page_res = post_form( +                    provider_redirect_page_res, 'Downloading Provider Login Page') +                mvpd_confirm_page_res = post_form(provider_login_page_res, 'Logging in', { +                    mso_info['username_field']: username, +                    mso_info['password_field']: password,                  }) -            provider_login_page_res = post_form( -                provider_redirect_page_res, 'Downloading Provider Login Page') -            login_data = {} -            if mso_id == 'DTV': -                login_data = { -                    'username': username, -                    'password': password, -                } -            elif mso_id == 'Rogers': -                login_data = { -                    'UserName': username, -                    'UserPassword': password, -                } -            mvpd_confirm_page_res = post_form(provider_login_page_res, 'Logging in', login_data) -            if mso_id == 'DTV': -                post_form(mvpd_confirm_page_res, 'Confirming Login') - -            session = self._download_webpage( -                self._SERVICE_PROVIDER_TEMPLATE % 'session', video_id, -                'Retrieving Session', data=urlencode_postdata({ -                    '_method': 'GET', -                    'requestor_id': requestor_id, -                }), headers=mvpd_headers) -            if '<pendingLogout' in session: -                self._downloader.cache.store('mvpd', requestor_id, {}) -                return self._extract_mvpd_auth(url, video_id, requestor_id, resource) -            authn_token = unescapeHTML(xml_text(session, 'authnToken')) -            requestor_info['authn_token'] = authn_token -            self._downloader.cache.store('mvpd', requestor_id, requestor_info) - -        authz_token = requestor_info.get(guid) -        if authz_token and is_expired(authz_token, 'simpleTokenTTL'): -            authz_token = None -        if not authz_token: -            authorize = self._download_webpage( -                self._SERVICE_PROVIDER_TEMPLATE % 'authorize', video_id, -                'Retrieving Authorization Token', data=urlencode_postdata({ -                    'resource_id': resource, +                if mso_id == 'DTV': +                    post_form(mvpd_confirm_page_res, 'Confirming Login') + +                session = self._download_webpage( +                    self._SERVICE_PROVIDER_TEMPLATE % 'session', video_id, +                    'Retrieving Session', data=urlencode_postdata({ +                        '_method': 'GET', +                        'requestor_id': requestor_id, +                    }), headers=mvpd_headers) +                if '<pendingLogout' in session: +                    self._downloader.cache.store('mvpd', requestor_id, {}) +                    count += 1 +                    continue +                authn_token = unescapeHTML(xml_text(session, 'authnToken')) +                requestor_info['authn_token'] = authn_token +                self._downloader.cache.store('mvpd', requestor_id, requestor_info) + +            authz_token = requestor_info.get(guid) +            if authz_token and is_expired(authz_token, 'simpleTokenTTL'): +                authz_token = None +            if not authz_token: +                authorize = self._download_webpage( +                    self._SERVICE_PROVIDER_TEMPLATE % 'authorize', video_id, +                    'Retrieving Authorization Token', data=urlencode_postdata({ +                        'resource_id': resource, +                        'requestor_id': requestor_id, +                        'authentication_token': authn_token, +                        'mso_id': xml_text(authn_token, 'simpleTokenMsoID'), +                        'userMeta': '1', +                    }), headers=mvpd_headers) +                if '<pendingLogout' in authorize: +                    self._downloader.cache.store('mvpd', requestor_id, {}) +                    count += 1 +                    continue +                authz_token = unescapeHTML(xml_text(authorize, 'authzToken')) +                requestor_info[guid] = authz_token +                self._downloader.cache.store('mvpd', requestor_id, requestor_info) + +            mvpd_headers.update({ +                'ap_19': xml_text(authn_token, 'simpleSamlNameID'), +                'ap_23': xml_text(authn_token, 'simpleSamlSessionIndex'), +            }) + +            short_authorize = self._download_webpage( +                self._SERVICE_PROVIDER_TEMPLATE % 'shortAuthorize', +                video_id, 'Retrieving Media Token', data=urlencode_postdata({ +                    'authz_token': authz_token,                      'requestor_id': requestor_id, -                    'authentication_token': authn_token, -                    'mso_id': xml_text(authn_token, 'simpleTokenMsoID'), -                    'userMeta': '1', +                    'session_guid': xml_text(authn_token, 'simpleTokenAuthenticationGuid'), +                    'hashed_guid': 'false',                  }), headers=mvpd_headers) -            if '<pendingLogout' in authorize: +            if '<pendingLogout' in short_authorize:                  self._downloader.cache.store('mvpd', requestor_id, {}) -                return self._extract_mvpd_auth(url, video_id, requestor_id, resource) -            authz_token = unescapeHTML(xml_text(authorize, 'authzToken')) -            requestor_info[guid] = authz_token -            self._downloader.cache.store('mvpd', requestor_id, requestor_info) - -        mvpd_headers.update({ -            'ap_19': xml_text(authn_token, 'simpleSamlNameID'), -            'ap_23': xml_text(authn_token, 'simpleSamlSessionIndex'), -        }) - -        short_authorize = self._download_webpage( -            self._SERVICE_PROVIDER_TEMPLATE % 'shortAuthorize', -            video_id, 'Retrieving Media Token', data=urlencode_postdata({ -                'authz_token': authz_token, -                'requestor_id': requestor_id, -                'session_guid': xml_text(authn_token, 'simpleTokenAuthenticationGuid'), -                'hashed_guid': 'false', -            }), headers=mvpd_headers) -        if '<pendingLogout' in short_authorize: -            self._downloader.cache.store('mvpd', requestor_id, {}) -            return self._extract_mvpd_auth(url, video_id, requestor_id, resource) -        return short_authorize +                count += 1 +                continue +            return short_authorize diff --git a/youtube_dl/extractor/common.py b/youtube_dl/extractor/common.py index 6edd5a769..ff19270ae 100644 --- a/youtube_dl/extractor/common.py +++ b/youtube_dl/extractor/common.py @@ -680,7 +680,7 @@ class InfoExtractor(object):          return (username, password) -    def _get_login_info(self): +    def _get_login_info(self, username_option='username', password_option='password', netrc_machine=None):          """          Get the login info as (username, password)          It will look in the netrc file using the _NETRC_MACHINE value @@ -694,11 +694,11 @@ class InfoExtractor(object):          downloader_params = self._downloader.params          # Attempt to use provided username and password or .netrc data -        if downloader_params.get('username') is not None: -            username = downloader_params['username'] -            password = downloader_params['password'] +        if downloader_params.get(username_option) is not None: +            username = downloader_params[username_option] +            password = downloader_params[password_option]          else: -            username, password = self._get_netrc_login_info() +            username, password = self._get_netrc_login_info(netrc_machine)          return (username, password) diff --git a/youtube_dl/options.py b/youtube_dl/options.py index c4057ce59..b99201a20 100644 --- a/youtube_dl/options.py +++ b/youtube_dl/options.py @@ -94,7 +94,7 @@ def parseOpts(overrideArguments=None):          setattr(parser.values, option.dest, value.split(','))      def _hide_login_info(opts): -        PRIVATE_OPTS = ['-p', '--password', '-u', '--username', '--video-password'] +        PRIVATE_OPTS = ['-p', '--password', '-u', '--username', '--video-password', '--ap-password', '--ap-username']          eqre = re.compile('^(?P<key>' + ('|'.join(re.escape(po) for po in PRIVATE_OPTS)) + ')=.+$')          def _scrub_eq(o): @@ -350,10 +350,28 @@ def parseOpts(overrideArguments=None):          '--video-password',          dest='videopassword', metavar='PASSWORD',          help='Video password (vimeo, smotri, youku)') -    authentication.add_option( + +    adobe_pass = optparse.OptionGroup(parser, 'Adobe Pass Options') +    adobe_pass.add_option(          '--ap-mso-id',          dest='ap_mso_id', metavar='APMSOID', -        help='Adobe Pass Multiple-system operator Identifier(DTV, Rogers)') +        help='Adobe Pass Multiple-system operator Identifier') +    adobe_pass.add_option( +        '--ap-username', +        dest='ap_username', metavar='APUSERNAME', +        help='TV Provider Login with this account ID') +    adobe_pass.add_option( +        '--ap-password', +        dest='ap_password', metavar='APPASSWORD', +        help='TV Provider Account password. If this option is left out, youtube-dl will ask interactively.') +    adobe_pass.add_option( +        '--list-ap-mso-ids', +        action='store_true', dest='list_ap_mso_ids', default=False, +        help='List all supported TV Providers') +    adobe_pass.add_option( +        '--ap-retries', +        dest='ap_retries', metavar='APRETRIES', default=3, +        help='Number of retries for Adobe Pass Authorization requests')      video_format = optparse.OptionGroup(parser, 'Video Format Options')      video_format.add_option( | 
