@@ -35,7 +35,8 @@ def __init__(self, base_url, **kwargs):
3535 """ Initialize class with initial parameters
3636
3737 Args:
38- base_url - Base API URL
38+ base_url - Base URL for the InvenTree server, including port (if required)
39+ e.g. "http://inventree.server.com:8000"
3940
4041 kwargs:
4142 username - Login username
@@ -45,18 +46,17 @@ def __init__(self, base_url, **kwargs):
4546 verbose - Print extra debug messages (default = False)
4647 """
4748
48- if not base_url .endswith ('/' ):
49- base_url += '/'
50-
51- # Server address *must* end with /api/
52- if not base_url .endswith ('/api/' ):
53- base_url = os .path .join (base_url , 'api' )
49+ # Strip out trailing "/api/" (if provided)
50+ if base_url .endswith ("/api/" ):
51+ base_url = base_url [:- 5 ]
5452
5553 if not base_url .endswith ('/' ):
5654 base_url += '/'
5755
5856 self .base_url = base_url
5957
58+ self .api_url = os .path .join (self .base_url , 'api/' )
59+
6060 logger .info ("Connecting to server: " + str (self .base_url ))
6161
6262 self .username = kwargs .get ('username' , None )
@@ -78,7 +78,7 @@ def __init__(self, base_url, **kwargs):
7878
7979 def clean_url (self , url ):
8080
81- url = os .path .join (self .base_url , url )
81+ url = os .path .join (self .api_url , url )
8282
8383 if not url .endswith ('/' ):
8484 url += '/'
@@ -97,7 +97,7 @@ def testServer(self):
9797 logger .info ("Checking InvenTree server connection..." )
9898
9999 try :
100- response = requests .get (self .base_url )
100+ response = requests .get (self .api_url )
101101 except requests .exceptions .ConnectionError :
102102 logger .error ("Server connection refused - check server address" )
103103 return False
@@ -144,7 +144,7 @@ def requestToken(self):
144144 logger .info ("Requesting auth token from server..." )
145145
146146 # Request an auth token from the server
147- token_url = os .path .join (self .base_url , 'user/token/' )
147+ token_url = os .path .join (self .api_url , 'user/token/' )
148148
149149 reply = requests .get (token_url , auth = self .auth )
150150
@@ -176,7 +176,7 @@ def request(self, url, **kwargs):
176176 if url .startswith ('/' ):
177177 url = url [1 :]
178178
179- api_url = os .path .join (self .base_url , url )
179+ api_url = os .path .join (self .api_url , url )
180180
181181 if not api_url .endswith ('/' ):
182182 api_url += '/'
@@ -426,3 +426,55 @@ def get(self, url, **kwargs):
426426 return None
427427
428428 return data
429+
430+ def downloadFile (self , url , destination ):
431+ """
432+ Download a file from the InvenTree server.
433+
434+ - If the "destination" is a directory, use the filename of the remote URL
435+ """
436+
437+ # Check that the provided URL is "absolute"
438+ if not url .startswith (self .base_url ):
439+
440+ if url .startswith ('/' ):
441+ url = url [1 :]
442+
443+ url = os .path .join (self .base_url , url )
444+
445+ if os .path .exists (destination ) and os .path .isdir (destination ):
446+
447+ destination = os .path .join (
448+ destination ,
449+ os .path .basename (url )
450+ )
451+
452+ destination = os .path .abspath (destination )
453+
454+ headers = {
455+ 'AUTHORIZATION' : f"Token { self .token } "
456+ }
457+
458+ with requests .get (url , stream = True , headers = headers ) as request :
459+
460+ if not request .status_code == 200 :
461+ logger .error (
462+ f"Error downloading file '{ url } ': Server returned status { request .status_code } "
463+ )
464+ return False
465+
466+ headers = request .headers
467+
468+ if 'text/html' in headers ['Content-Type' ]:
469+ logger .error (
470+ f"Error downloading file '{ url } ': Server return invalid response (text/html)"
471+ )
472+ return False
473+
474+ with open (destination , 'wb' ) as f :
475+
476+ for chunk in request .iter_content (chunk_size = 16 * 1024 ):
477+ f .write (chunk )
478+
479+ logger .info (f"Downloaded '{ url } ' to '{ destination } '" )
480+ return True
0 commit comments