U
    gX                     @   s  d Z ddlZddlZddlmZ ddlmZmZmZm	Z	m
Z
mZ ddlmZmZ ddlmZmZ ddlmZmZ eeZG dd	 d	Zeed
ddZee	e d
ddZeee	e dddZeeedddZeed
ddZeed
ddZee	e d
ddZ edddZ!e	e
e dddZ"e	e#d d!d"Z$e	e#d d#d$Z%e&d%d&d'Z'e&ed(d)d*Z(e&e#d(d+d,Z)e&e#d(d-d.Z*e&ed(d/d0Z+e&e#d(d1d2Z,e&e#d(d3d4Z-e&e#d(d5d6Z.d=e&e#d7d8d9Z/eed:d;d<Z0dS )>aX  
This module contains all logic necessary to decipher the signature.

YouTube's strategy to restrict downloading videos is to send a ciphered version
of the signature to the client, along with the decryption algorithm obfuscated
in JavaScript. For the clients to play the videos, JavaScript must take the
ciphered version, cycle it through a series of "transform functions," and then
signs the media URL with the output.

This module is responsible for (1) finding and extracting those "transform
functions" (2) maps them to Python equivalents and (3) taking the ciphered
signature and decoding it.

    N)chain)AnyCallableDictListOptionalTuple)ExtractErrorRegexMatchError)cacheregex_search)find_object_from_startpointthrottling_array_splitc                   @   sT   e Zd ZedddZedddZeeddd	Zeee	ee
f d
ddZdS )Cipherjsc                 C   s|   t || _td}|| jd }|s6td|jd|dd d }t||| _	ddg| _
t|| _t|| _d | _d S )Nz^\w+\Wr   __init__Zcallerpatternz\w+\.(\w+)\(\w,(\d+)\)z\w+\[(\"\w+\")\]\(\w,(\d+)\))get_transform_plantransform_planrecompilesearchr
   r   groupget_transform_maptransform_mapjs_func_patternsget_throttling_planthrottling_planget_throttling_function_arraythrottling_arraycalculated_n)selfr   Z	var_regexZ	var_matchvar r&   1/tmp/pip-unpacked-wheel-1a9f0fi6/pytube/cipher.pyr      s     

 

zCipher.__init__)	initial_nc                 C   s   | j r| j S tt| jD ]}| j| dkr|| j|< q| jD ]}| jt|d  }t|st| d td| j d t	| d| jt|d  }t|dkr|| q>t|dkr>| jt|d  }||| q>d	
|| _ | j S )
z6Converts n to the correct value to prevent throttling.br   z is not callable.zThrottling array:

          )r#   rangelenr"   r    intcallableloggerdebugr	   join)r$   r(   istepZ	curr_funcZ	first_argZ
second_argr&   r&   r'   calculate_n0   s&    

zCipher.calculate_n)ciphered_signaturereturnc              	   C   sZ   t |}| jD ]@}| |\}}| j| ||}tdd|||| j|  qd|S )aA  Decipher the signature.

        Taking the ciphered signature, applies the transform functions.

        :param str ciphered_signature:
            The ciphered signature sent in the ``player_config``.
        :rtype: str
        :returns:
            Decrypted signature required to download the media content.
        zOapplied transform function
output: %s
js_function: %s
argument: %d
function: %sr.   )listr   parse_functionr   r3   r4   r5   )r$   r9   	signaturejs_funcnameargumentr&   r&   r'   get_signatureL   s    
zCipher.get_signaturer>   r:   c                 C   sZ   t d | jD ]8}t|}||}|r| \}}|t|f  S qtddddS )a  Parse the Javascript transform function.

        Break a JavaScript transform function down into a two element ``tuple``
        containing the function name and some integer-based argument.

        :param str js_func:
            The JavaScript version of the transform function.
        :rtype: tuple
        :returns:
            two element tuple containing the function name and an argument.

        **Example**:

        parse_function('DE.AJ(a,15)')
        ('AJ', 15)

        zparsing transform functionr<   r   r   N)	r3   r4   r   r   r   r   groupsr1   r
   )r$   r>   r   regexZparse_matchfn_nameZfn_argr&   r&   r'   r<   j   s    



 zCipher.parse_functionN)__name__
__module____qualname__strr   r;   r8   rA   r   r   r1   r<   r&   r&   r&   r'   r      s
   r   )r   r:   c                 C   sr   ddddddddd	d
ddg}t d |D ]6}t|}|| }|r*t d| |d  S q*tddddS )zExtract the name of the function responsible for computing the signature.
    :param str js:
        The contents of the base.js asset file.
    :rtype: str
    :returns:
        Function name from regex match
    zX\b[cs]\s*&&\s*[adf]\.set\([^,]+\s*,\s*encodeURIComponent\s*\(\s*(?P<sig>[a-zA-Z0-9$]+)\(zg\b[a-zA-Z0-9]+\s*&&\s*[a-zA-Z0-9]+\.set\([^,]+\s*,\s*encodeURIComponent\s*\(\s*(?P<sig>[a-zA-Z0-9$]+)\(zi(?:\b|[^a-zA-Z0-9$])(?P<sig>[a-zA-Z0-9$]{2})\s*=\s*function\(\s*a\s*\)\s*{\s*a\s*=\s*a\.split\(\s*""\s*\)zS(?P<sig>[a-zA-Z0-9$]+)\s*=\s*function\(\s*a\s*\)\s*{\s*a\s*=\s*a\.split\(\s*""\s*\)z1(["\'])signature\1\s*,\s*(?P<sig>[a-zA-Z0-9$]+)\(z!\.sig\|\|(?P<sig>[a-zA-Z0-9$]+)\(zyt\.akamaized\.net/\)\s*\|\|\s*.*?\s*[cs]\s*&&\s*[adf]\.set\([^,]+\s*,\s*(?:encodeURIComponent\s*\()?\s*(?P<sig>[a-zA-Z0-9$]+)\(z>\b[cs]\s*&&\s*[adf]\.set\([^,]+\s*,\s*(?P<sig>[a-zA-Z0-9$]+)\(zM\b[a-zA-Z0-9]+\s*&&\s*[a-zA-Z0-9]+\.set\([^,]+\s*,\s*(?P<sig>[a-zA-Z0-9$]+)\(zH\bc\s*&&\s*a\.set\([^,]+\s*,\s*\([^)]*\)\s*\(\s*(?P<sig>[a-zA-Z0-9$]+)\(zS\bc\s*&&\s*[a-zA-Z0-9]+\.set\([^,]+\s*,\s*\([^)]*\)\s*\(\s*(?P<sig>[a-zA-Z0-9$]+)\(zfinding initial function name"finished regex search, matched: %sr+   get_initial_function_namemultipler   N)r3   r4   r   r   r   r   r
   )r   function_patternsr   rD   function_matchr&   r&   r'   rK      s0    



 rK   c                 C   s4   t t| }d| }td t|| dddS )a  Extract the "transform plan".

    The "transform plan" is the functions that the ciphered signature is
    cycled through to obtain the actual signature.

    :param str js:
        The contents of the base.js asset file.

    **Example**:

    ['DE.AJ(a,15)',
    'DE.VR(a,3)',
    'DE.AJ(a,51)',
    'DE.VR(a,3)',
    'DE.kT(a,51)',
    'DE.kT(a,8)',
    'DE.VR(a,3)',
    'DE.kT(a,21)']
    z.%s=function\(\w\){[a-z=\.\(\"\)]*;(.*);(?:.+)}zgetting transform planr+   )r   ;)r   escaperK   r3   r4   r   split)r   r?   r   r&   r&   r'   r      s    
r   )r   r%   r:   c                 C   sZ   dt | }td t j|t jd}|| }|sBtd|d|d	dd
d	S )
a  Extract the "transform object".

    The "transform object" contains the function definitions referenced in the
    "transform plan". The ``var`` argument is the obfuscated variable name
    which contains these functions, for example, given the function call
    ``DE.AJ(a,15)`` returned by the transform plan, "DE" would be the var.

    :param str js:
        The contents of the base.js asset file.
    :param str var:
        The obfuscated variable name that stores an object with all functions
        that descrambles the signature.

    **Example**:

    >>> get_transform_object(js, 'DE')
    ['AJ:function(a){a.reverse()}',
    'VR:function(a,b){a.splice(0,b)}',
    'kT:function(a,b){var c=a[0];a[0]=a[b%a.length];a[b]=c}']

    zvar %s={(.*?)};zgetting transform object)flagsget_transform_objectr   r+   r*    z, )r   rP   r3   r4   r   DOTALLr   r
   r   replacerQ   )r   r%   r   rD   Ztransform_matchr&   r&   r'   rS      s    

rS   c                 C   s<   t | |}i }|D ]$}|dd\}}t|}|||< q|S )aW  Build a transform function lookup.

    Build a lookup table of obfuscated JavaScript function names to the
    Python equivalents.

    :param str js:
        The contents of the base.js asset file.
    :param str var:
        The obfuscated variable name that stores an object with all functions
        that descrambles the signature.

    :r+   )rS   rQ   map_functions)r   r%   Ztransform_objectmapperobjr?   functionfnr&   r&   r'   r      s    

r   c              	   C   s   dg}t d |D ]}t|}|| }|rt d| t| dkrZ|d  S |d}|r|d}tdj	t
|dd| }|r|ddd	}d
d |D }|t|   S qtddddS )zExtract the name of the function that computes the throttling parameter.

    :param str js:
        The contents of the base.js asset file.
    :rtype: str
    :returns:
        The name of the function used to compute the throttling parameter.
    zha\.[a-zA-Z]\s*&&\s*\([a-z]\s*=\s*a\.get\("n"\)\)\s*&&\s*\([a-z]\s*=\s*([a-zA-Z0-9$]+)(\[\d+\])?\([a-z]\)z Finding throttling function namerJ   r+   r,   z[]zvar {nfunc}\s*=\s*(\[.+?\]);)Znfunc,c                 S   s   g | ]}|  qS r&   )strip).0xr&   r&   r'   
<listcomp>%  s     z0get_throttling_function_name.<locals>.<listcomp>get_throttling_function_namerL   r   N)r3   r4   r   r   r   r0   rC   r   r^   formatrP   rQ   r1   r
   )r   rM   r   rD   rN   idxarrayr&   r&   r'   rb      s6    




 rb   c                 C   sZ   t t| }d| }t |}|| }t| | d d}d|}|	d| S )zExtract the raw code for the throttling function.

    :param str js:
        The contents of the base.js asset file.
    :rtype: str
    :returns:
        The name of the function used to compute the throttling parameter.
    z%s=function\(\w\)r+   r*   r.   r   )
r   rP   rb   r   r   r   spanrQ   r5   r   )r   r?   Zpattern_startrD   matchZcode_lines_listZjoined_linesr&   r&   r'   get_throttling_function_code-  s    



rh   c                 C   sj  t | }d}t|}||}t|| d d }t|}g }|D ]}z|t| W qFW n t	k
rt   Y nX |dkr|d qF|
dr|dr||dd  qF|
dr0dtfd	tfd
tfdtfdtfdtfdtfdtfdtff	}	d}
|	D ]&\}}t||r || d}
q |
r0qF|| qFtt|D ]}|| dkrH|||< qH|S )zExtract the "c" array.

    :param str js:
        The contents of the base.js asset file.
    :returns:
        The array of various integers, arrays, and functions.
    z,c=\[r+   nullN"r   r[   zS{for\(\w=\(\w%\w\.length\+\w\.length\)%\w\.length;\w--;\)\w\.unshift\(\w.pop\(\)\)}{\w\.reverse\(\)}z{\w\.push\(\w\)}z.;var\s\w=\w\[0\];\w\[0\]=\w\[\w\];\w\[\w\]=\w}z	case\s\d+z1\w\.splice\(0,1,\w\.splice\(\w,1,\w\[0\]\)\[0\]\)z;\w\.splice\(\w,1\)}zL\w\.splice\(-\w\)\.reverse\(\)\.forEach\(function\(\w\){\w\.unshift\(\w\)}\)zBfor\(var \w=\w\.length;\w;\)\w\.push\(\w\.splice\(--\w,1\)\[0\]\)}FT)rh   r   r   r   r   rf   r   appendr1   
ValueError
startswithendswiththrottling_unshiftthrottling_reversethrottling_pushthrottling_swapthrottling_cipher_functionthrottling_nested_splice	js_splicethrottling_prependr/   r0   )r   raw_codeZarray_startZarray_regexrg   Z	array_rawZ	str_arrayZconverted_arrayelrY   foundr   r\   r6   r&   r&   r'   r!   F  sT    



r!   r   c           
      C   s   t | }d}t|}||}t|| d d }d}t|}||}g }	|D ]D}|d dkr|	|d |d |d f qV|	|d |d f qV|	S )a  Extract the "throttling plan".

    The "throttling plan" is a list of tuples used for calling functions
    in the c array. The first element of the tuple is the index of the
    function to call, and any remaining elements of the tuple are arguments
    to pass to that function.

    :param str js:
        The contents of the base.js asset file.
    :returns:
        The full function code for computing the throttlign parameter.
    ztry{r+   z(c\[(\d+)\]\(c\[(\d+)\](,c(\[(\d+)\]))?\)   r.   r   )rh   r   r   r   r   rf   findallrl   )
r   rx   Ztransform_startZ
plan_regexrg   Ztransform_plan_rawZ
step_startZ
step_regexmatchesZtransform_stepsr&   r&   r'   r     s    



r   arr_c                 C   s   | ddd S )a@  Reverse elements in a list.

    This function is equivalent to:

    .. code-block:: javascript

        function(a, b) { a.reverse() }

    This method takes an unused ``b`` variable as their transform functions
    universally sent two arguments.

    **Example**:

    >>> reverse([1, 2, 3, 4])
    [4, 3, 2, 1]
    Nr   r&   r~   r&   r&   r'   reverse  s    r   r   r)   c                 C   s   | |d S )zAdd/remove items to/from a list.

    This function is equivalent to:

    .. code-block:: javascript

        function(a, b) { a.splice(0, b) }

    **Example**:

    >>> splice([1, 2, 3, 4], 2)
    [1, 2]
    Nr&   r   r&   r&   r'   splice  s    r   c                 C   s>   |t |  }tt| | g| d| | d g| |d d S )zSwap positions at b modulus the list length.

    This function is equivalent to:

    .. code-block:: javascript

        function(a, b) { var c=a[0];a[0]=a[b%a.length];a[b]=c }

    **Example**:

    >>> swap([1, 2, 3, 4], 2)
    [3, 2, 1, 4]
    r+   r   N)r0   r;   r   )r   r)   rr&   r&   r'   swap  s    r   )r   c                 C   s4   |   ddd }tt|D ]}|| | |< qdS )zReverses the input list.

    Needs to do an in-place reversal so that the passed list gets changed.
    To accomplish this, we create a reversed copy, and then change each
    indvidual element.
    Nr   )copyr/   r0   )r   Zreverse_copyr6   r&   r&   r'   rq     s    rq   dec                 C   s   |  | dS )zPushes an element onto a list.N)rl   r   r&   r&   r'   rr     s    rr   c                 C   s   |t |  t |  t |  S )zPerform the modular function from the throttling array functions.

    In the javascript, the modular operation is as follows:
    e = (e % d.length + d.length) % d.length

    We simply translate this to python here.
    )r0   r   r&   r&   r'   throttling_mod_func  s    r   c                 C   sF   t | |}| | d | d|   }|   |D ]}| | q2dS )zRotates the elements of the list to the right.

    In the javascript, the operation is as follows:
    for(e=(e%d.length+d.length)%d.length;e--;)d.unshift(d.pop())
    N)r   clearrl   )r   r   new_arrry   r&   r&   r'   rp     s
    
rp   c           	      C   s   t d}d}t |}|  }t|D ]V\}}|||||  | d | t| }|||  || | |< |d8 }q$dS )a  This ciphers d with e to generate a new list.

    In the javascript, the operation is as follows:
    var h = [A-Za-z0-9-_], f = 96;  // simplified from switch-case loop
    d.forEach(
        function(l,m,n){
            this.push(
                n[m]=h[
                    (h.indexOf(l)-h.indexOf(this[m])+m-32+f--)%h.length
                ]
            )
        },
        e.split("")
    )
    z@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_`       r+   N)r;   r   	enumerateindexr0   rl   )	r   r   hfthisZcopied_listmlZbracket_valr&   r&   r'   rt     s    ,rt   c                 C   s2   t | |}t| |d| d }t| dd|d  dS )a  Nested splice function in throttling js.

    In the javascript, the operation is as follows:
    function(d,e){
        e=(e%d.length+d.length)%d.length;
        d.splice(
            0,
            1,
            d.splice(
                e,
                1,
                d[0]
            )[0]
        )
    }

    While testing, all this seemed to do is swap element 0 and e,
    but the actual process is preserved in case there was an edge
    case that was not considered.
    r+   r   N)r   rv   )r   r   Zinner_splicer&   r&   r'   ru   -  s    
ru   c                 C   sb   t | }t| |}| | d | d|   }|   |D ]}| | q:t | }||ks^tdS )a;  

    In the javascript, the operation is as follows:
    function(d,e){
        e=(e%d.length+d.length)%d.length;
        d.splice(-e).reverse().forEach(
            function(f){
                d.unshift(f)
            }
        )
    }

    Effectively, this moves the last e elements of d to the beginning.
    N)r0   r   r   rl   AssertionError)r   r   Z	start_lenr   ry   Zend_lenr&   r&   r'   rw   Q  s    
rw   c                 C   s*   t | |}| d }| | | d< || |< dS )z6Swap positions of the 0'th and e'th elements in-place.r   N)r   )r   r   r   r&   r&   r'   rs   p  s    
rs   )r   startc                 G   s   z,|t | krt | }|dk r*t | | }W n tk
rD   d}Y nX |rZ|t | | krft | | }| |||  }| d| t| | || d  }|   |D ]}| | q|S )a  Implementation of javascript's splice function.

    :param list arr:
        Array to splice
    :param int start:
        Index at which to start changing the array
    :param int delete_count:
        Number of elements to delete from the array
    :param *items:
        Items to add to the array

    Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice  # noqa:E501
    r   N)r0   	TypeErrorr;   r   rl   )r   r   Zdelete_countitemsZdeleted_elementsr   ry   r&   r&   r'   rv   x  s    
$rv   rB   c                 C   sN   dt fdtfdtfdtff}|D ]\}}t|| r |  S q tddddS )	zFor a given JavaScript transform function, return the Python equivalent.

    :param str js_func:
        The JavaScript version of the transform function.
    rk   z{\w\.splice\(0,\w\)}z9{var\s\w=\w\[0\];\w\[0\]=\w\[\w\%\w.length\];\w\[\w\]=\w}zD{var\s\w=\w\[0\];\w\[0\]=\w\[\w\%\w.length\];\w\[\w\%\w.length\]=\w}rX   rL   r   N)r   r   r   r   r   r
   )r>   rY   r   r\   r&   r&   r'   rX     s    
rX   )N)1__doc__loggingr   	itertoolsr   typingr   r   r   r   r   r   Zpytube.exceptionsr	   r
   Zpytube.helpersr   r   Zpytube.parserr   r   	getLoggerrF   r3   r   rI   rK   r   rS   r   rb   rh   r!   r   r   r1   r   r   r;   rq   rr   r   rp   rt   ru   rw   rs   rv   rX   r&   r&   r&   r'   <module>   s<    
o$ .B#"$*