mirror of
				https://gitlab.com/ytdl-org/youtube-dl.git
				synced 2025-11-04 00:27:07 -05:00 
			
		
		
		
	[youtube] Fix authentication (closes #12927)
This commit is contained in:
		@@ -37,8 +37,7 @@ from ..utils import (
 | 
			
		||||
    parse_codecs,
 | 
			
		||||
    parse_duration,
 | 
			
		||||
    remove_quotes,
 | 
			
		||||
    remove_start,
 | 
			
		||||
    sanitized_Request,
 | 
			
		||||
    # remove_start,
 | 
			
		||||
    smuggle_url,
 | 
			
		||||
    str_to_int,
 | 
			
		||||
    try_get,
 | 
			
		||||
@@ -54,7 +53,16 @@ class YoutubeBaseInfoExtractor(InfoExtractor):
 | 
			
		||||
    """Provide base functions for Youtube extractors"""
 | 
			
		||||
    _LOGIN_URL = 'https://accounts.google.com/ServiceLogin'
 | 
			
		||||
    _TWOFACTOR_URL = 'https://accounts.google.com/signin/challenge'
 | 
			
		||||
    _PASSWORD_CHALLENGE_URL = 'https://accounts.google.com/signin/challenge/sl/password'
 | 
			
		||||
 | 
			
		||||
    _LOOKUP_URL = 'https://accounts.google.com/_/signin/sl/lookup'
 | 
			
		||||
    _LOOKUP_REQ_TEMPLATE = '["{0}",null,[],null,"US",null,null,2,false,true,[null,null,[2,1,null,1,"https://accounts.google.com/ServiceLogin?passive=true&continue=https%3A%2F%2Fwww.youtube.com%2Fsignin%3Fnext%3D%252F%26action_handle_signin%3Dtrue%26hl%3Den%26app%3Ddesktop%26feature%3Dsign_in_button&hl=en&service=youtube&uilel=3&requestPath=%2FServiceLogin&Page=PasswordSeparationSignIn",null,[],4],1,[null,null,[]],null,null,null,true],"{0}"]'
 | 
			
		||||
 | 
			
		||||
    _PASSWORD_CHALLENGE_URL = 'https://accounts.google.com/_/signin/sl/challenge'
 | 
			
		||||
    _PASSWORD_CHALLENGE_REQ_TEMPLATE = '["{0}",null,1,null,[1,null,null,null,["{1}",null,true]],[null,null,[2,1,null,1,"https://accounts.google.com/ServiceLogin?passive=true&continue=https%3A%2F%2Fwww.youtube.com%2Fsignin%3Fnext%3D%252F%26action_handle_signin%3Dtrue%26hl%3Den%26app%3Ddesktop%26feature%3Dsign_in_button&hl=en&service=youtube&uilel=3&requestPath=%2FServiceLogin&Page=PasswordSeparationSignIn",null,[],4],1,[null,null,[]],null,null,null,true]]'
 | 
			
		||||
 | 
			
		||||
    _TFA_URL = 'https://accounts.google.com/_/signin/challenge'
 | 
			
		||||
    _TFA_REQ_TEMPLATE = '["{0}",null,2,null,[9,null,null,null,null,null,null,null,[null,"{1}",false,2]]]'
 | 
			
		||||
 | 
			
		||||
    _NETRC_MACHINE = 'youtube'
 | 
			
		||||
    # If True it will raise an error if no login info is provided
 | 
			
		||||
    _LOGIN_REQUIRED = False
 | 
			
		||||
@@ -96,72 +104,76 @@ class YoutubeBaseInfoExtractor(InfoExtractor):
 | 
			
		||||
 | 
			
		||||
        login_form = self._hidden_inputs(login_page)
 | 
			
		||||
 | 
			
		||||
        login_form.update({
 | 
			
		||||
            'checkConnection': 'youtube',
 | 
			
		||||
            'Email': username,
 | 
			
		||||
            'Passwd': password,
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        login_results = self._download_webpage(
 | 
			
		||||
            self._PASSWORD_CHALLENGE_URL, None,
 | 
			
		||||
            note='Logging in', errnote='unable to log in', fatal=False,
 | 
			
		||||
            data=urlencode_postdata(login_form))
 | 
			
		||||
        if login_results is False:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        error_msg = self._html_search_regex(
 | 
			
		||||
            r'<[^>]+id="errormsg_0_Passwd"[^>]*>([^<]+)<',
 | 
			
		||||
            login_results, 'error message', default=None)
 | 
			
		||||
        if error_msg:
 | 
			
		||||
            raise ExtractorError('Unable to login: %s' % error_msg, expected=True)
 | 
			
		||||
 | 
			
		||||
        if re.search(r'id="errormsg_0_Passwd"', login_results) is not None:
 | 
			
		||||
            raise ExtractorError('Please use your account password and a two-factor code instead of an application-specific password.', expected=True)
 | 
			
		||||
 | 
			
		||||
        # Two-Factor
 | 
			
		||||
        # TODO add SMS and phone call support - these require making a request and then prompting the user
 | 
			
		||||
 | 
			
		||||
        if re.search(r'(?i)<form[^>]+id="challenge"', login_results) is not None:
 | 
			
		||||
            tfa_code = self._get_tfa_info('2-step verification code')
 | 
			
		||||
 | 
			
		||||
            if not tfa_code:
 | 
			
		||||
                self._downloader.report_warning(
 | 
			
		||||
                    'Two-factor authentication required. Provide it either interactively or with --twofactor <code>'
 | 
			
		||||
                    '(Note that only TOTP (Google Authenticator App) codes work at this time.)')
 | 
			
		||||
                return False
 | 
			
		||||
 | 
			
		||||
            tfa_code = remove_start(tfa_code, 'G-')
 | 
			
		||||
 | 
			
		||||
            tfa_form_strs = self._form_hidden_inputs('challenge', login_results)
 | 
			
		||||
 | 
			
		||||
            tfa_form_strs.update({
 | 
			
		||||
                'Pin': tfa_code,
 | 
			
		||||
                'TrustDevice': 'on',
 | 
			
		||||
        def req(url, f_req, note, errnote):
 | 
			
		||||
            data = login_form.copy()
 | 
			
		||||
            data.update({
 | 
			
		||||
                'pstMsg': 1,
 | 
			
		||||
                'checkConnection': 'youtube',
 | 
			
		||||
                'checkedDomains': 'youtube',
 | 
			
		||||
                'hl': 'en',
 | 
			
		||||
                'deviceinfo': '[null,null,null,[],null,"US",null,null,[],"GlifWebSignIn",null,[null,null,[]]]',
 | 
			
		||||
                'f.req': f_req,
 | 
			
		||||
                'flowName': 'GlifWebSignIn',
 | 
			
		||||
                'flowEntry': 'ServiceLogin',
 | 
			
		||||
            })
 | 
			
		||||
            return self._download_json(
 | 
			
		||||
                url, None, note=note, errnote=errnote,
 | 
			
		||||
                transform_source=lambda s: re.sub(r'^[^[]*', '', s),
 | 
			
		||||
                fatal=False,
 | 
			
		||||
                data=urlencode_postdata(data), headers={
 | 
			
		||||
                    'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
 | 
			
		||||
                    'Google-Accounts-XSRF': 1,
 | 
			
		||||
                })
 | 
			
		||||
 | 
			
		||||
            tfa_data = urlencode_postdata(tfa_form_strs)
 | 
			
		||||
        lookup_results = req(
 | 
			
		||||
            self._LOOKUP_URL, self._LOOKUP_REQ_TEMPLATE.format(username),
 | 
			
		||||
            'Looking up account info', 'Unable to look up account info')
 | 
			
		||||
 | 
			
		||||
            tfa_req = sanitized_Request(self._TWOFACTOR_URL, tfa_data)
 | 
			
		||||
            tfa_results = self._download_webpage(
 | 
			
		||||
                tfa_req, None,
 | 
			
		||||
                note='Submitting TFA code', errnote='unable to submit tfa', fatal=False)
 | 
			
		||||
 | 
			
		||||
            if tfa_results is False:
 | 
			
		||||
                return False
 | 
			
		||||
 | 
			
		||||
            if re.search(r'(?i)<form[^>]+id="challenge"', tfa_results) is not None:
 | 
			
		||||
                self._downloader.report_warning('Two-factor code expired or invalid. Please try again, or use a one-use backup code instead.')
 | 
			
		||||
                return False
 | 
			
		||||
            if re.search(r'(?i)<form[^>]+id="gaia_loginform"', tfa_results) is not None:
 | 
			
		||||
                self._downloader.report_warning('unable to log in - did the page structure change?')
 | 
			
		||||
                return False
 | 
			
		||||
            if re.search(r'smsauth-interstitial-reviewsettings', tfa_results) is not None:
 | 
			
		||||
                self._downloader.report_warning('Your Google account has a security notice. Please log in on your web browser, resolve the notice, and try again.')
 | 
			
		||||
                return False
 | 
			
		||||
 | 
			
		||||
        if re.search(r'(?i)<form[^>]+id="gaia_loginform"', login_results) is not None:
 | 
			
		||||
            self._downloader.report_warning('unable to log in: bad username or password')
 | 
			
		||||
        if lookup_results is False:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        user_hash = lookup_results[0][2]
 | 
			
		||||
 | 
			
		||||
        password_challenge_results = req(
 | 
			
		||||
            self._PASSWORD_CHALLENGE_URL,
 | 
			
		||||
            self._PASSWORD_CHALLENGE_REQ_TEMPLATE.format(user_hash, password),
 | 
			
		||||
            'Logging in', 'Unable to log in')[0]
 | 
			
		||||
 | 
			
		||||
        if password_challenge_results is False:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        msg = password_challenge_results[5]
 | 
			
		||||
        if msg is not None and isinstance(msg, list):
 | 
			
		||||
            raise ExtractorError('Unable to login: %s' % msg[5], expected=True)
 | 
			
		||||
 | 
			
		||||
        password_challenge_results = password_challenge_results[-1]
 | 
			
		||||
 | 
			
		||||
        # tfa = password_challenge_results[0]
 | 
			
		||||
        # if isinstance(tfa, list) and tfa[0][2] == 'TWO_STEP_VERIFICATION':
 | 
			
		||||
        #     tfa_code = self._get_tfa_info('2-step verification code')
 | 
			
		||||
        #
 | 
			
		||||
        #     if not tfa_code:
 | 
			
		||||
        #         self._downloader.report_warning(
 | 
			
		||||
        #             'Two-factor authentication required. Provide it either interactively or with --twofactor <code>'
 | 
			
		||||
        #             '(Note that only TOTP (Google Authenticator App) codes work at this time.)')
 | 
			
		||||
        #         return False
 | 
			
		||||
        #
 | 
			
		||||
        #     tfa_code = remove_start(tfa_code, 'G-')
 | 
			
		||||
        #     print('tfa', tfa_code)
 | 
			
		||||
        #     tfa_results = req(
 | 
			
		||||
        #         self._TFA_URL,
 | 
			
		||||
        #         self._TFA_REQ_TEMPLATE.format(user_hash, tfa_code),
 | 
			
		||||
        #         'Submitting TFA code', 'Unable to submit TFA code')
 | 
			
		||||
        #
 | 
			
		||||
        #     TODO
 | 
			
		||||
 | 
			
		||||
        check_cookie_results = self._download_webpage(
 | 
			
		||||
            password_challenge_results[2], None, 'Checking cookie')
 | 
			
		||||
 | 
			
		||||
        if '>Sign out<' not in check_cookie_results:
 | 
			
		||||
            self._downloader.report_warning('Unable to log in')
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def _real_initialize(self):
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user