88import  concurrent .futures 
99
1010from  .utils  import  Utils 
11- from  .exceptions  import  DownloadException , WatermarkIDDownloadException , AssetNotFullyUploaded 
11+ from  .exceptions  import  (
12+   DownloadException ,
13+   WatermarkIDDownloadException ,
14+   AssetNotFullyUploaded ,
15+   AssetChecksumNotPresent ,
16+   AssetChecksumMismatch 
17+ )
1218
1319thread_local  =  threading .local ()
1420
1521class  FrameioDownloader (object ):
16-   def  __init__ (self , asset , download_folder , prefix ,  multi_part = False , concurrency = 5 ,  replace = False ):
22+   def  __init__ (self , asset , download_folder , prefix = None ,  replace = False , checksum_verification = True ,  multi_part = False ,  concurrency = 5 ):
1723    self .multi_part  =  multi_part 
1824    self .asset  =  asset 
1925    self .asset_type  =  None 
@@ -29,8 +35,10 @@ def __init__(self, asset, download_folder, prefix, multi_part=False, concurrency
2935    self .prefix  =  prefix 
3036    self .filename  =  Utils .normalize_filename (asset ["name" ])
3137    self .replace  =  replace 
38+     self .checksum_verification  =  checksum_verification 
3239
3340    self ._evaluate_asset ()
41+     self ._get_path ()
3442
3543  def  _evaluate_asset (self ):
3644    if  self .asset .get ("_type" ) !=  "file" :
@@ -45,19 +53,39 @@ def _get_session(self):
4553    return  thread_local .session 
4654
4755  def  _create_file_stub (self ):
56+     if  self .replace  ==  True :
57+       os .remove (self .destination ) # Remove the file 
58+       self ._create_file_stub () # Create a new stub 
59+       
4860    try :
4961      fp  =  open (self .destination , "w" )
5062      # fp.write(b"\0" * self.file_size) # Disabled to prevent pre-allocatation of disk space 
5163      fp .close ()
52-     except  FileExistsError  as  e :
53-       if  self .replace  ==  True :
54-         os .remove (self .destination ) # Remove the file 
55-         self ._create_file_stub () # Create a new stub 
56-       else :
57-         print (e )
58-         raise  e 
64+ 
65+     except  Exception  as  e :
66+       raise  e 
67+ 
5968    return  True 
6069
70+   def  _get_path (self ):
71+     print ("prefix:" , self .prefix )
72+     if  self .prefix  !=  None :
73+       self .filename  =  self .prefix  +  self .filename 
74+ 
75+     if  self .destination  ==  None :
76+       final_destination  =  os .path .join (self .download_folder , self .filename )
77+       self .destination  =  final_destination 
78+       
79+     return  self .destination 
80+ 
81+   def  _get_checksum (self ):
82+     try :
83+       self .original_checksum  =  self .asset ['checksums' ]['xx_hash' ]
84+     except  (TypeError , KeyError ):
85+       self .original_checksum  =  None 
86+     
87+     return  self .original_checksum 
88+ 
6189  def  get_download_key (self ):
6290    try :
6391      url  =  self .asset ['original' ]
@@ -84,26 +112,27 @@ def get_download_key(self):
84112
85113    return  url 
86114
87-   def  get_path (self ):
88-     if  self .prefix  !=  None :
89-       self .filename  =  self .prefix  +  self .filename 
115+   def  download_handler (self ):
116+     if  os .path .isfile (self .destination ) and  self .replace  !=  True :
117+       try :
118+         raise  FileExistsError 
119+       except  NameError :
120+         raise  OSError ('File exists' )  # Python < 3.3 
90121
91-     if  self .destination  ==  None :
92-       final_destination  =  os .path .join (self .download_folder , self .filename )
93-       self .destination  =  final_destination 
94-       
95-     return  self .destination 
122+     url  =  self .get_download_key ()
96123
97-   def  download_handler (self ):
98-     if  os .path .isfile (self .get_path ()):
99-       print ("File already exists at this location." )
100-       return  self .destination 
124+     if  self .watermarked  ==  True :
125+       return  self .download (url )
101126    else :
102-       url  =  self .get_download_key ()
103- 
104-       if  self .watermarked  ==  True :
127+       # Don't use multi-part download for files below 25 MB 
128+       if  self .asset ['filesize' ] <  26214400 :
105129        return  self .download (url )
130+       if  self .multi_part  ==  True :
131+         return  self .multi_part_download (url )
106132      else :
133+         # Don't use multi-part download for files below 25 MB 
134+         if  self .asset ['filesize' ] <  26214400 :
135+           return  self .download (url )
107136        if  self .multi_part  ==  True :
108137          return  self .multi_part_download (url )
109138        else :
@@ -114,8 +143,17 @@ def download(self, url):
114143    print ("Beginning download -- {} -- {}" .format (self .asset ["name" ], Utils .format_bytes (self .file_size , type = "size" )))
115144
116145    # Downloading 
117-     r  =  requests .get (url )
118-     open (self .destination , "wb" ).write (r .content )
146+     session  =  self ._get_session ()
147+     r  =  session .get ('GET' , url , stream = True )
148+ 
149+     with  open (self .destination , 'wb' ) as  handle :
150+       try :
151+         # TODO make sure this approach works for SBWM download 
152+         for  chunk  in  r .iter_content (chunk_size = 4096 ):
153+           if  chunk :
154+             handle .write (chunk )
155+       except  requests .exceptions .ChunkedEncodingError  as  e :
156+         raise  e 
119157
120158    download_time  =  time .time () -  start_time 
121159    download_speed  =  Utils .format_bytes (math .ceil (self .file_size / (download_time )))
@@ -161,7 +199,17 @@ def multi_part_download(self, url):
161199    download_speed  =  Utils .format_bytes (math .ceil (self .file_size / (download_time )))
162200    print ("Downloaded {} at {}" .format (Utils .format_bytes (self .file_size , type = "size" ), download_speed ))
163201
164-     return  self .destination 
202+     if  self .checksum_verification  ==  True :
203+       # Check for checksum, if not present throw error 
204+       if  self ._get_checksum () ==  None :
205+         raise  AssetChecksumNotPresent 
206+       else :
207+         if  Utils .calculate_hash (self .destination ) !=  self .original_checksum :
208+           raise  AssetChecksumMismatch 
209+         else :
210+           return  self .destination 
211+     else :
212+       return  self .destination 
165213
166214  def  download_chunk (self , task ):
167215    # Download a particular chunk 
0 commit comments