B
    ÛÞß]pe  ã               @   s  d dl Z d dlZd dlZd dlZd dlZd dlZd dlZd dlZd dl	Z	d dl
Z
d dlZd dlmZ d dlmZ d dlmZ d dlZddlmZ ddlmZ ddlmZ dd	gZdZd
Zd
Zd eee¡ZG dd„ deƒZ G dd„ deƒZ!G dd„ dƒZ"dd„ Z#dS )é    N)ÚStringIO)Úc_inté   )Úutils)ÚControlThread)ÚdownloadÚSmartDLr   é   z{}.{}.{}c               @   s(   e Zd ZdZdd„ Zdd„ Zdd„ ZdS )	ÚHashFailedExceptionzRaised when hash check fails.c             C   s   || _ || _|| _d S )N)ÚfilenameÚcalculated_hashÚneeded_hash)ÚselfÚfnZ	calc_hashr   © r   úIC:\Users\ibrahim.fayad\Desktop\sentSatProg_v.0.1.3\pySmartDL\pySmartDL.pyÚ__init__   s    zHashFailedException.__init__c             C   s   d  | j| j| j¡S )Nz,HashFailedException({}, got {}, expected {}))Úformatr   r   r   )r   r   r   r   Ú__str__   s    zHashFailedException.__str__c             C   s   d  | j| j| j¡S )Nz-<HashFailedException {}, got {}, expected {}>)r   r   r   r   )r   r   r   r   Ú__repr__!   s    zHashFailedException.__repr__N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   r   r   r   r   r   r
      s   r
   c               @   s(   e Zd ZdZdd„ Zdd„ Zdd„ ZdS )	ÚCanceledExceptionz Raised when the job is canceled.c             C   s   d S )Nr   )r   r   r   r   r   &   s    zCanceledException.__init__c             C   s   dS )Nr   r   )r   r   r   r   r   (   s    zCanceledException.__str__c             C   s   dS )Nz<CanceledException>r   )r   r   r   r   r   *   s    zCanceledException.__repr__N)r   r   r   r   r   r   r   r   r   r   r   r   $   s   r   c               @   s   e Zd ZdZdIdd„Zd	d
„ Zdd„ Zdd„ Zdd„ Zdd„ Z	dJdd„Z
dd„ ZdKdd„ZdLdd„ZdMdd„ZdNdd„Zd d!„ ZdOd$d%„Zd&d'„ Zd(d)„ Zd*d+„ Zd,d-„ ZdPd.d/„Zd0d1„ Zd2d3„ Zd4d5„ Zd6d7„ Zd8d9„ Zd:d;„ ZdQd<d=„ZdRd>d?„ZdSd@dA„ZdTdCdD„Z dEdF„ Z!dGdH„ Z"dS )Ur   am  
    The main SmartDL class
    
    :param urls: Download url. It is possible to pass unsafe and unicode characters. You can also pass a list of urls, and those will be used as mirrors.
    :type urls: string or list of strings
    :param dest: Destination path. Default is `%TEMP%/pySmartDL/`.
    :type dest: string
    :param progress_bar: If True, prints a progress bar to the `stdout stream <http://docs.python.org/2/library/sys.html#sys.stdout>`_. Default is `True`.
    :type progress_bar: bool
	:param fix_urls: If true, attempts to fix urls with unsafe characters.
	:type fix_urls: bool
	:param threads: Number of threads to use.
	:type threads: int
    :param timeout: Timeout for network operations, in seconds. Default is 5.
	:type timeout: int
    :param logger: An optional logger.
    :type logger: `logging.Logger` instance
    :param connect_default_logger: If true, connects a default logger to the class.
    :type connect_default_logger: bool
    :param request_args: Arguments to be passed to a new urllib.request.Request instance in dictionary form. See `urllib.request docs <https://docs.python.org/3/library/urllib.request.html#urllib.request.Request>`_ for options. 
    :type request_args: dict
    :rtype: `SmartDL` instance
    
    .. NOTE::
            The provided dest may be a folder or a full path name (including filename). The workflow is:
            
            * If the path exists, and it's an existing folder, the file will be downloaded to there with the original filename.
            * If the past does not exist, it will create the folders, if needed, and refer to the last section of the path as the filename.
            * If you want to download to folder that does not exist at the moment, and want the module to fill in the filename, make sure the path ends with `os.sep`.
            * If no path is provided, `%TEMP%/pySmartDL/` will be used.
    NTé   é   Fc             C   s  |r|| _ n|rt ¡ | _ n
t ¡ | _ |	rDd|	kr<tƒ |	d< |	| _ndtƒ i| _d| jd krld| jd d< |r€|r€|  ||¡ t|tƒr|gn|| _	|r¬dd„ | j	D ƒ| _	| j	 
d¡| _| j  d | j¡¡ tj tj tj | j¡j¡¡}|ptj t ¡ d|¡| _| jd	 tjkrntj | jd d	… ¡r`tj | jd d	… ¡r`t | jd d	… ¡ |  j|7  _tj | j¡rtj | j|¡| _|| _|| _|| _ d
| _!d| _"d| _#d| _$t% &t'd¡| _(i | _)d| _*d| _+d| _,d| _-d| _.g | _/|| _0|
| _1d | _2d | _3tj tj 4| j¡¡sR| j  d tj 4| j¡¡¡ t 5tj 4| j¡¡ tj6| j| j| j ds|| j  7d¡ d
| _tj | j¡r | j  7d | j¡¡ tj tj 4| j¡¡sè| j  7d tj 4| j¡¡¡ t 5tj 4| j¡¡ | j  d | j¡¡ t 8| j¡| _9d S )NÚheadersz
User-AgentzNMozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0c             S   s   g | ]}t  |¡‘qS r   )r   Zurl_fix)Ú.0Úxr   r   r   ú
<listcomp>c   s    z$SmartDL.__init__.<locals>.<listcomp>r   zUsing url "{}"Ú	pySmartDLéÿÿÿÿr   é   i    ÚreadyFTz'Folder "{}" does not exist. Creating...)r   Útimeoutz=Server does not support HTTPRange. threads_count is set to 1.z?Destination "{}" already exists. Existing file will be removed.z-Directory "{}" does not exist. Creating it...z&Creating a ThreadPool of {} thread(s).):Úloggerr   Zcreate_debugging_loggerZDummyLoggerÚdictÚrequestArgsÚadd_basic_authenticationÚ
isinstanceÚstrÚmirrorsÚpopÚurlÚinfor   ÚurllibÚparseÚunquoteÚosÚpathÚbasenameÚurlparseÚjoinÚtempfileÚ
gettempdirÚdestÚsepÚexistsÚisfileÚunlinkÚisdirÚprogress_barÚthreads_countr%   Úcurrent_attempÚattemps_limitÚminChunkFileÚfilesizeÚmultiprocessingÚValuer   Ú
shared_varÚthread_shared_cmdsÚstatusÚverify_hashÚ_killedÚ_failedÚ_start_func_blockingÚerrorsÚhash_algorithmÚ	hash_codeÚpost_threadpool_threadÚcontrol_threadÚdirnameÚmakedirsZauto_detect_HTTPRange_supportÚwarningZManagedThreadPoolExecutorÚpool)r   Zurlsr:   r@   Zfix_urlsÚthreadsr%   r&   Zconnect_default_loggerZrequest_argsZhash_checksumZ	hash_algoÚusernameÚpasswordr   r   r   r   r   N   sv    

 0zSmartDL.__init__c             C   s   d  | j| j¡S )NzSmartDL(r"{}", dest=r"{}"))r   r.   r:   )r   r   r   r   r   ”   s    zSmartDL.__str__c             C   s   d  | j¡S )Nz<SmartDL {}>)r   r.   )r   r   r   r   r   —   s    zSmartDL.__repr__c             C   s2   d  ||¡}t | d¡¡}d| | jd d< dS )zØ
        Uses HTTP Basic Access authentication for the connection.
        
        :param username: Username.
        :type username: string
        :param password: Password.
        :type password: string
        z{}:{}zutf-8s   Basic r   ÚAuthorizationN)r   Úbase64Ústandard_b64encodeÚencoder(   )r   rY   rZ   Zauth_stringZbase64stringr   r   r   r)   š   s    	z SmartDL.add_basic_authenticationc             C   s   d| _ || _|| _dS )aÒ  
        Adds hash verification to the download.
        
        If hash is not correct, will try different mirrors. If all mirrors aren't
        passing hash verification, `HashFailedException` Exception will be raised.
        
        .. NOTE::
            If downloaded file already exist on the destination, and hash matches, pySmartDL will not download it again.
            
        .. WARNING::
            The hashing algorithm must be supported on your system, as documented at `hashlib documentation page <http://docs.python.org/3/library/hashlib.html>`_.
        
        :param algorithm: Hashing algorithm.
        :type algorithm: string
        :param hash: Hash code.
        :type hash: string
        TN)rK   rP   rQ   )r   Ú	algorithmÚhashr   r   r   Úadd_hash_verification§   s    zSmartDL.add_hash_verificationc          	   C   sú   dddg}t j | j¡}t j | j¡}| j d¡ xÂ|D ]º}yšd||f }tjj	|f| j
Ž}tj |¡}| ¡  d¡}| ¡  xT|D ]L}	| ¡ |	 ¡ kr„| j d| ¡ | d¡}
|	 d	¡d
 }|  |
|¡ dS q„W W q8 tjjk
rð   w8Y q8X q8W dS )a4  
        Will attempt to fetch UNIX hash sums files (`SHA256SUMS`, `SHA1SUMS` or `MD5SUMS` files in
        the same url directory).
        
        Calls `self.add_hash_verification` if successful. Returns if a matching hash was found.
        
        :rtype: bool
        
        *New in 1.2.1*
        Z
SHA256SUMSZSHA1SUMSZMD5SUMSzLooking for SUMS files...z%s/%sÚ
zFound a matching hash in %sZSUMSú r   N)r3   r4   rT   r.   r5   r&   r/   r0   ÚrequestÚRequestr(   ÚurlopenÚreadÚsplitÚcloseÚlowerÚrstripra   ÚerrorÚ	HTTPError)r   Zdefault_sums_filenamesZfolderZorig_basenamer   Zsums_urlZsumsRequestÚobjÚdataÚlineZalgor`   r   r   r   Úfetch_hash_sums¾   s(    



zSmartDL.fetch_hash_sumsc       	         sV  ˆ j dkstd ˆ j ¡ƒ‚ˆ j d¡ |dkr6ˆ j}n|ˆ _ˆ jr\ˆ j d tˆ jƒ¡¡ nˆ j d¡ ˆ jdk	rºt	j
 ˆ j¡rºt ˆ jˆ j¡ ¡ ˆ j ¡ krºˆ j dˆ j ¡ dˆ _ dS ˆ j d	 ˆ jˆ j¡¡ tjjˆ jfˆ jŽ}ytjj|ˆ jd
}W n¸ tjjtjjtjfk
r´ } zˆˆ j |¡ ˆ jrzˆ j d t|ƒ¡¡ ˆ j d¡ˆ _ˆ j d ˆ j¡¡ ˆ   |¡ dS ˆ j !t|ƒ¡ ˆ j |¡ dˆ _"dˆ _ ‚ W dd}~X Y nX y2t#|j$d ƒˆ _%ˆ j d ˆ j%t &ˆ j%¡¡¡ W n. t't(t)fk
r   ˆ j !d¡ dˆ _%Y nX t *ˆ j%ˆ j+ˆ j,¡}|d d |d d  d }t|ƒdkrvˆ j d t|ƒt &|¡¡¡ nˆ j d t &|¡¡¡ dˆ _ xVt-|ƒD ]J\}}ˆ j. /t0ˆ jˆ jd|  ˆ j|d |d ˆ jdˆ j1ˆ j2ˆ jd¡}qžW t3j4t5ˆ j.‡ fdd„t6t|ƒƒD ƒˆ jgˆ j%ˆ fdˆ _7dˆ j7_8ˆ j7  ¡  t9ˆ ƒˆ _:|rRˆ j;dd dS )a,  
        Starts the download task. Will raise `RuntimeError` if it's the object's already downloading.
        
        .. warning::
            If you're using the non-blocking mode, Exceptions won't be raised. In that case, call
            `isSuccessful()` after the task is finished, to make sure the download succeeded. Call
            `get_errors()` to get the the exceptions.
        
        :param blocking: If true, calling this function will block the thread until the download finished. Default is *True*.
        :type blocking: bool
        r$   z#cannot start (current status is {})z!Starting a new SmartDL operation.Nz"One URL and {} mirrors are loaded.zOne URL is loaded.zKDestination '%s' already exists, and the hash matches. No need to download.ÚfinishedzDownloading '{}' to '{}'...)r%   z{} Trying next mirror...r   zUsing url "{}"TzContent-LengthzContent-Length is {} ({}).z8Server did not send Content-Length. Filesize is unknown.r   z+Launching {} threads (downloads {}/thread).z"Launching 1 thread (downloads {}).Údownloadingz.%.3dFc                s   g | ]}ˆ j d |  ‘qS )z.%.3d)r:   )r   Úi)r   r   r   r    8  s    z!SmartDL.start.<locals>.<listcomp>)ÚtargetÚargs)Úraise_exceptions)<rJ   ÚRuntimeErrorr   r&   r/   rN   r,   ÚlenrQ   r3   r4   r<   r:   r   Úget_file_hashrP   rj   r.   r0   rd   re   r(   rf   r%   rl   rm   ÚURLErrorÚsocketrO   Úappendr+   r-   ÚstartrV   rM   Úintr   rE   Úsizeof_humanÚ
IndexErrorÚKeyErrorÚ	TypeErrorZcalc_chunk_sizerA   rD   Ú	enumeraterW   Zsubmitr   rH   rI   Ú	threadingÚThreadÚpost_threadpool_actionsÚrangerR   Údaemonr   rS   Úwait)	r   ÚblockingÚreqZurlObjÚerv   Zbytes_per_threadrt   Úargr   )r   r   r~   á   sˆ    
"
" 

zSmartDL.startc             C   s$   | j  |d ¡ | j |d ¡ d S )Nr   r   )rO   r}   r&   Ú	exception)r   rŒ   r   r   r   r   Ú_exc_callbackE  s    zSmartDL._exc_callbackÚ c          	   C   sx   | j | jk r8|  j d7  _ d| _d| j_i | _|  ¡  n<d}|rN|d |¡7 }| j 	t
j | jd|i tƒ ¡¡ d| _d S )Nr   r$   r   z"The maximum retry attempts reachedz ({})Ú0T)rB   rC   rJ   rH   ÚvaluerI   r~   r   rO   r}   r0   rl   rm   r.   r   rM   )r   ZeStrÚsr   r   r   ÚretryI  s    
 zSmartDL.retryc             C   sf   | j rP|r| j |¡ d| _d| j_| j  d¡| _| j 	d 
| j¡¡ |  ¡  nd| _| j |¡ d S )Nr$   r   zUsing url "{}"T)r,   rO   r}   rJ   rH   r“   r-   r.   r&   r/   r   r~   rM   )r   r   r   r   r   Útry_next_mirrorX  s    
zSmartDL.try_next_mirrorc             C   s*   |r t  | j ¡ ¡}|r|S dS | j ¡ S )aŒ  
        Get estimated time of download completion, in seconds. Returns `0` if there is
        no enough data to calculate the estimated time (this will happen on the approx.
        first 5 seconds of each download).
        
        :param human: If true, returns a human-readable formatted string. Else, returns an int type number
        :type human: bool
        :rtype: int/string
        ZTBD)r   Ú
time_humanrS   Úget_eta)r   Úhumanr”   r   r   r   r˜   e  s    
zSmartDL.get_etac             C   s$   |rd  t | j ¡ ¡¡S | j ¡ S )zê
        Get current transfer speed in bytes per second.
        
        :param human: If true, returns a human-readable formatted string. Else, returns an int type number
        :type human: bool
        :rtype: int/string
        z{}/s)r   r   r€   rS   Ú	get_speed)r   r™   r   r   r   rš   t  s    zSmartDL.get_speedc             C   s2   | j s
dS | j ¡ | j kr.d| j ¡  | j  S dS )z~
        Returns the current progress of the download, as a float between `0` and `1`.
        
        :rtype: float
        r   g      ð?)rE   rS   Úget_dl_size)r   r   r   r   Úget_progress€  s
    zSmartDL.get_progress©ú-ú#é   c             C   s   t  |  ¡ ||¡S )a[  
        Returns the current progress of the download as a string containing a progress bar.
        
        .. NOTE::
            That's an alias for pySmartDL.utils.progress_bar(obj.get_progress()).
        
        :param length: The length of the progress bar in chars. Default is 20.
        :type length: int
        :rtype: string
        )r   r@   rœ   )r   Z	look_feelÚlengthr   r   r   Úget_progress_barŒ  s    zSmartDL.get_progress_barc             C   s(   | j dkrdS | j dkrdS | j ¡  S )zP
        Returns if the task is finished.
        
        :rtype: bool
        r$   Frr   T)rJ   rR   Úis_alive)r   r   r   r   Ú
isFinished™  s
    

zSmartDL.isFinishedc             C   sP   | j r
dS d}x8| jdkrF|d7 }t d¡ |dkrtd | j¡ƒ‚qW | j S )aÙ  
        Returns if the download is successfull. It may fail in the following scenarios:
        
        - Hash check is enabled and fails.
        - All mirrors are down.
        - Any local I/O problems (such as `no disk space available`).
        
        .. NOTE::
            Call `get_errors()` to get the exceptions, if any.
        
        Will raise `RuntimeError` if it's called when the download task is not finished yet.
        
        :rtype: bool
        Fr   rr   r   gš™™™™™¹?é   z]The download task must be finished in order to see if it's successful. (current status is {}))rL   rJ   ÚtimeÚsleeprx   r   rM   )r   Únr   r   r   ÚisSuccessful¥  s    
zSmartDL.isSuccessfulc             C   s   | j S )zo
        Get errors happened while downloading.
        
        :rtype: list of `Exception` instances
        )rO   )r   r   r   r   Ú
get_errorsÁ  s    zSmartDL.get_errorsc             C   s   | j S )z­
        Returns the current status of the task. Possible values: *ready*,
        *downloading*, *paused*, *combining*, *finished*.
        
        :rtype: string
        )rJ   )r   r   r   r   Ú
get_statusÉ  s    zSmartDL.get_statusc             C   sR   | j dkrdS x|  ¡ s$t d¡ qW | j ¡  | j ¡  | jrN|rN| jd ‚dS )zÉ
        Blocks until the download is finished.
        
        :param raise_exceptions: If true, this function will raise exceptions. Default is *False*.
        :type raise_exceptions: bool
        )r$   rr   Ngš™™™™™¹?r"   )	rJ   r¤   r¦   r§   rR   r7   rS   rM   rO   )r   rw   r   r   r   rŠ   Ò  s    




zSmartDL.waitc             C   s   | j dkrd| jd< d| _dS )z%
        Stops the download.
        rs   r‘   ÚstopTN)rJ   rI   rL   )r   r   r   r   r¬   ä  s    

zSmartDL.stopc             C   s   | j dkrd| _ d| jd< dS )z&
        Pauses the download.
        rs   Úpausedr‘   ÚpauseN)rJ   rI   )r   r   r   r   r®   ì  s    
zSmartDL.pausec             C   s   |   ¡  dS )z<
        Continues the download. same as unpause().
        N)Úunpause)r   r   r   r   Úresumeô  s    zSmartDL.resumec             C   s&   | j dkr"d| jkr"d| _ | jd= dS )z;
        Continues the download. same as resume().
        r­   r®   rs   N)rJ   rI   )r   r   r   r   r¯   ú  s    zSmartDL.unpausec             C   sT   | j dkr$|dkr|  ¡  n|  ¡  |dkr>|| j | jd< nd| jkrP| jd= dS )zÐ
        Limits the download transfer speed.
        
        :param speed: Speed in bytes per download per second. Negative values will not limit the speed. Default is `-1`.
        :type speed: int
        rs   r   ÚlimitN)rJ   r®   r¯   rA   rI   )r   Zspeedr   r   r   Úlimit_speed  s    


zSmartDL.limit_speedc             C   s   | j S )z¼
        Get the destination path of the downloaded file. Needed when no
        destination is provided to the class, and exists on a temp folder.
        
        :rtype: string
        )r:   )r   r   r   r   Úget_dest  s    zSmartDL.get_destc             C   s(   | j s
dS |rt | j  ¡ ¡S | j  ¡ S )a*  
        Returns how much time did the download take, in seconds. Returns
        `-1` if the download task is not finished yet.

        :param human: If true, returns a human-readable formatted string. Else, returns an int type number
        :type human: bool
        :rtype: int/string
        r   )rS   r   r—   Úget_dl_time)r   r™   r   r   r   r´     s
    	zSmartDL.get_dl_timec             C   s(   | j s
dS |rt | j  ¡ ¡S | j  ¡ S )zá
        Get downloaded bytes counter in bytes.
        
        :param human: If true, returns a human-readable formatted string. Else, returns an int type number
        :type human: bool
        :rtype: int/string
        r   )rS   r   r€   r›   )r   r™   r   r   r   r›   +  s
    zSmartDL.get_dl_sizec             C   s(   | j s
dS |rt | j  ¡ ¡S | j  ¡ S )zÜ
        Get total download size in bytes.
        
        :param human: If true, returns a human-readable formatted string. Else, returns an int type number
        :type human: bool
        :rtype: int/string
        r   )rS   r   r€   Úget_final_filesize)r   r™   r   r   r   rµ   9  s
    zSmartDL.get_final_filesizer"   c          	   C   s\   | j dkrtd| j  ƒ‚|r dnd}t|  ¡ |ƒ }|dkrF| |¡n| ¡ }W dQ R X |S )až  
        Returns the downloaded data. Will raise `RuntimeError` if it's
        called when the download task is not finished yet.
        
        :param binary: If true, will read the data as binary. Else, will read it as text.
        :type binary: bool
        :param bytes: Number of bytes to read. Negative values will read until EOF. Default is `-1`.
        :type bytes: int
        :rtype: string
        rr   zTThe download task must be finished in order to read the data. (current status is %s)ÚrbÚrr   N)rJ   rx   Úopenr³   rg   )r   ÚbinaryÚbytesÚflagsÚfro   r   r   r   Úget_dataH  s    
$zSmartDL.get_datac             C   s   t  || jdd¡ ¡ S )a¿  
        Returns the downloaded data's hash. Will raise `RuntimeError` if it's
        called when the download task is not finished yet.
        
        :param algorithm: Hashing algorithm.
        :type algorithm: bool
        :rtype: string
        
        .. WARNING::
            The hashing algorithm must be supported on your system, as documented at `hashlib documentation page <http://docs.python.org/3/library/hashlib.html>`_.
        T)r¹   )ÚhashlibÚnewr½   Ú	hexdigest)r   r_   r   r   r   Úget_data_hash[  s    zSmartDL.get_data_hashc             C   s   |   ¡ }t |¡S )a  
        Returns the JSON in the downloaded data. Will raise `RuntimeError` if it's
        called when the download task is not finished yet. Will raise `json.decoder.JSONDecodeError`
        if the downloaded data is not valid JSON.
        
        :rtype: dict
        )r½   ÚjsonÚloads)r   ro   r   r   r   Úget_jsoni  s    zSmartDL.get_json)NTTr   r   NFNNNNN)N)r‘   )N)F)F)r   r    )F)F)F)F)Fr"   )#r   r   r   r   r   r   r   r)   ra   rq   r~   r   r•   r–   r˜   rš   rœ   r¢   r¤   r©   rª   r«   rŠ   r¬   r®   r°   r¯   r²   r³   r´   r›   rµ   r½   rÁ   rÄ   r   r   r   r   r   -   s@   
F#
d




	




c             C   sZ  x|   ¡ st d¡ qW |jr"dS |  ¡ rZx|  ¡ D ]}|j |¡ q4W | t	|  ¡ ƒ¡ |j
rp|j d¡ dS |rÚt|d ƒ}tdd„ |d D ƒƒ}t || ¡}|d| krÚd ||||¡}|j |¡ | |¡ dS d	|_|j d
¡ tj|Ž  |jrV|d }	t |j|	¡}
|
|jkr.|j d¡ n(|j d¡ | ttj |	¡t|jƒ¡ dS )z=Run function after thread pool is done. Run this in a thread.gš™™™™™¹?NzTask had errors. Exiting...r   c             S   s   g | ]}t j |¡‘qS r   )r3   r4   Úgetsize)r   r   r   r   r   r    ˆ  s    z+post_threadpool_actions.<locals>.<listcomp>i   znDiff between downloaded files and expected filesizes is {}B (filesize: {}, expected_filesize: {}, {} threads).Ú	combiningzCombining downloaded file.r"   zHash verification succeeded.zHash verification failed.)Údoner¦   r§   rL   Zget_exceptionZget_exceptionsr&   r   r•   r+   rM   rV   ry   ÚsumÚmathÚfabsr   rJ   r/   r   Zcombine_filesrK   rz   rP   rQ   r–   r
   r3   r4   r5   r`   )rW   rv   Zexpected_filesizeZ
SmartDLObjÚexcrX   Ztotal_filesizeZdiffZerrMsgZ	dest_pathZhash_r   r   r   r‡   t  s<    


r‡   )$r3   Úurllib.requestr0   Úurllib.errorÚurllib.parser…   r¦   rÉ   r8   r\   r¾   r|   Úior   Zmultiprocessing.dummyÚdummyrF   Úctypesr   rÂ   r‘   r   rS   r   r   Ú__all__Z__version_mjaor__Z__version_minor__Z__version_micro__r   Ú__version__Ú	Exceptionr
   r   r   r‡   r   r   r   r   Ú<module>   s8   	    K